C++20 更明確的初始化:Designated Initializers


Designated initializers 是 C++20 新的一種針對物件的成員資料初始化的方法。他基本上是 aggregate initialization(參考)的一種,然後算是透過加上引數名稱的形式、提高了物件初始化的可閱讀性。

像是如果有一個結構定義是:

struct STest
{
  int  iVal1 = 0;
  int  iVal2 = 0;
};

以往要初始化的話,基本上就是寫成下面的形式:

STest s{ 1,2 };

如果在成員資料多的時候,其實會很難去判斷哪個引述是對應到哪個成員;同時,也沒辦法略過前面的項目,只設定後面的資料。此外,如果日後有修改結構的話,也很容易出問題。

而透過新的 designated initializers 的寫法,這邊的初始化可以變得更明確、更有彈性;他的寫法基本上如下:

STest x{ .iVal1 = 1, .iVal2{2} };

基本上,就是可以透過 .iVal1 這樣的形式,直接指定是要設定哪個資料成員的值了;而這邊的「iVal1」就叫做「designator」,他只能用在非 static 的成員上,而指定值的方法可以用「=」,也可以用「{}」的形式。

透過這樣的寫法,就可以更明確地指定成員資料的值了;而這樣的語法,也可以跳過前面的成員,只指定後面的成員。

STest x{.iVal2 = 2 };

這樣就可以只設定 iVal2 的值、讓 iVal1 維持是預設值的 0。

在個人來看,這樣的寫法最大的好處,就是好閱讀、同時也有一定程度的彈性了。

而參考《Designated Initializers in C++20》的話,則可以看到一個可能還滿實際的例子:

struct Time { int hour; int minute; };
struct Date { Time t; int year; int month; int day; };

Date d{
  .t {.hour = 10, .minute = 35 },
  .year = 2050, .month = 5, .day = 10
};

在這種情境下,要使用 Date 這個結構,初始話就可以變得更明確了。


另一方面,這樣在調整定義的時候,也可以減少問題。

例如,假設有一天把前面的 STest 改成下面的樣子:

struct STest
{
  int  iVal0 = 0;
  int  iVal1 = 0;
  int  iVal2 = 0;
};

也就是前面多了一個 iVal0。這個時候,前面本來結果一樣的寫法,現在雖然都還可以邊一,但是會產生出不同的結果:

STest s{ 1, 2 };                   // 1,2,0
STest x{ .iVal1 = 1, .iVal2{2} };  // 0,1,2

在這個狀況下,使用 designated initializers 的寫法,應該是更為明確的!

同時,對應其他形式的修改(例如調整成員的順序),也是更能反映出問題的。


另外相對地,他在使用也有一些限制:

  • 順序要按照宣告的順序
  • 不能用在 static data member
  • 只能用在 aggregate initialization
  • 不能巢狀使用

所以,如果上面的例子寫成:

STest x{ .iVal2 = 2, .iVal1 = 1 };

這樣的話,就會因為順序和宣告的不符合,而無法正確編譯。

不過老實說,個人是覺得都給了變數名稱還要限制順序,感覺還滿蠢的…

而不能巢狀使用,則是指不能使用下面的寫法:

Date d2{
  .t.hour = 10, .t.minute = 35, // ERROR!!
  .year = 2050, .month = 5, .day = 10
};

這樣的寫法,基本上還是 aggregate initialization 的一種形式,所以必須要符合 aggregate type 的條件才有機會使用。而在 C++20 的定義是:

  • 陣列
  • 類別(class / struct)
    • 不包含 private、protected 的非靜態資料
    • 沒有使用者定義的建構子(包含繼承來的)
    • 沒有 virtual、private、protected 繼承
    • 沒有 virtual 的成員函式

所以如果是一個有定義建構子的類別,就不能用 designated initializers 的寫法了。


參考:《Designated Initializers in C++20

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.