聚合初始化
使用初始化列表初始化聚合型別。它是一種 列表初始化(C++11 起)形式。
目錄 |
[編輯] 語法
T 物件 = { arg1, arg2, ... }; |
(1) | ||||||||
T 物件 { arg1, arg2, ... }; |
(2) | (C++11 起) | |||||||
T 物件 = { . des1 = arg1 , . des2 { arg2 } ... }; |
(3) | (C++20 起) | |||||||
T 物件 { . des1 = arg1 , . des2 { arg2 } ... }; |
(4) | (C++20 起) | |||||||
[編輯] 定義
[編輯] 聚合體
一個“聚合體”是以下型別之一:
- 陣列型別
- 類型別,且其具有
|
(C++11 前) |
(C++11 起) (C++20 前) | |
|
(C++20 起) |
- 沒有私有或保護的直接非靜態資料成員
|
(C++17 前) |
(C++17 起) |
- 沒有虛成員函式
|
(C++11 起) (直至 C++14) |
[編輯] 元素
聚合體的“元素”是
- 對於陣列,按遞增下標順序排列的陣列元素,或者
|
(C++17 前) |
(C++17 起) |
[編輯] 歸屬
花括號包圍的初始化列表中的每個初始化子句據說“歸屬於”正在被初始化的聚合體的一個元素,或者其某個子聚合體的一個元素。
考慮初始化子句的序列,以及最初形成作為正在被初始化的聚合體的元素序列並可能如下所述修改的聚合體元素序列
- 對於每個初始化子句,如果滿足以下任一條件,則它歸屬於相應的聚合體元素 elem
- elem 不是聚合體。
- 初始化子句以 { 開頭。
- 初始化子句是一個表示式,並且可以形成一個隱式轉換序列,將表示式轉換為 elem 的型別。
- elem 是一個自身沒有聚合體元素的聚合體。
- 否則,elem 是一個聚合體,並且該子聚合體在其自身聚合體元素的序列中被替換到聚合體元素列表中,歸屬分析從第一個這樣的元素和相同的初始化子句繼續。換句話說,這些規則遞迴地應用於聚合體的子聚合體。
當所有初始化子句都已用盡時,分析完成。如果任何初始化子句仍然沒有歸屬於聚合體或其某個子聚合體的元素,則程式格式錯誤。
struct S1 { int a, b; }; struct S2 { S1 s, t; }; // Each subaggregate of “x” is appertained to an initializer clause starting with { S2 x[2] = { // appertains to “x[0]” { {1, 2}, // appertains to “x[0].s” {3, 4} // appertains to “x[0].t” }, // appertains to “x[1]” { {5, 6}, // appertains to “x[1].s” {7, 8} // appertains to “x[1].t” } }; // “x” and “y” have the same value (see below) S2 y[2] = {1, 2, 3, 4, 5, 6, 7, 8}; // The process of the appertainment analysis of “y”: // 1. Initializes the aggregate element sequence (x[0], x[1]) and // the initializer clause sequence (1, 2, 3, 4, 5, 6, 7, 8). // 2. Starting from the first elements of each sequence, // checks whether 1 appertains to x[0]: // · x[0] is an aggregate. // · 1 does not begin with {. // · 1 is an expression, but it cannot be implicitly converted to S2. // · x[0] has aggregate elements. // 3. 0 cannot appertain to x[0], therefore x[0] is replaced by x[0].s and x[0].t, // the aggregate element sequence becomes (x[0].s, x[0].t, x[1]). // 4. Resumes the appertainment check, but 1 cannot appertain to x[0].s either. // 5. The aggregate element sequence now becomes (x[0].s.a, x[0].s.b, x[0].t, x[1]). // 6. Resumes the appertainment check again: // 1 appertains to x[0].s.a, and 2 appertains to x[0].s.b. // 7. The rest of the appertainment analysis works similarly. char cv[4] = {'a', 's', 'd', 'f', 0}; // Error: too many initializer clauses
[編輯] 初始化過程
[編輯] 確定元素種類
聚合初始化的效果是
|
(C++20 起) |
- 否則,如果初始化列表非空,則聚合體的顯式初始化元素是具有歸屬初始化子句的元素以及具有帶有歸屬初始化子句的子聚合體的元素。
- 否則,初始化列表必須為空 ({}),並且沒有顯式初始化元素。
- 如果聚合體是聯合體且存在兩個或更多顯式初始化元素,則程式格式錯誤
union u { int a; const char* b; }; u a = {1}; // OK: explicitly initializes member `a` u b = {0, "asdf"}; // error: explicitly initializes two members u c = {"asdf"}; // error: int cannot be initialized by "asdf" // C++20 designated initializer lists u d = {.b = "asdf"}; // OK: can explicitly initialize a non-initial member u e = {.a = 1, .b = "asdf"}; // error: explicitly initializes two members
[編輯] 顯式初始化元素
對於每個顯式初始化元素
struct C { union { int a; const char* p; }; int x; } c = {.a = 1, .x = 3}; // initializes c.a with 1 and c.x with 3
|
(C++20 起) |
|
(C++20 前) |
|
(C++20 起) |
- 如果初始化子句歸屬於聚合體元素,則聚合體元素從初始化子句中進行複製初始化。
- 否則,聚合體元素從一個花括號包圍的初始化列表進行復制初始化,該列表由所有歸屬於聚合體子物件的初始化子句按出現順序組成。
struct A { int x; struct B { int i; int j; } b; } a = {1, {2, 3}}; // initializes a.x with 1, a.b.i with 2, a.b.j with 3 struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1{{1, 2}, {}, 4}; // initializes d1.b1 with 1, d1.b2 with 2, // d1.b3 with 42, d1.d with 4 derived d2{{}, {}, 4}; // initializes d2.b1 with 0, d2.b2 with 42, // d2.b3 with 42, d2.d with 4
[編輯] 隱式初始化元素
對於非聯合體聚合體,每個未顯式初始化的元素按如下方式初始化:
|
(C++11 起) |
- 否則,如果元素不是引用,則元素從空初始化列表進行複製初始化。
- 否則,程式格式錯誤。
struct S { int a; const char* b; int c; int d = b[a]; }; // initializes ss.a with 1, // ss.b with "asdf", // ss.c with the value of an expression of the form int{} (that is, 0), // and ss.d with the value of ss.b[ss.a] (that is, 's') S ss = {1, "asdf"};
如果聚合體是聯合體且初始化列表為空,則
|
(C++11 起) |
- 否則,聯合體的第一個成員(如果有)從空初始化列表進行復制初始化。
[編輯] 大小未知的陣列
使用花括號包圍的初始化列表初始化的未知大小陣列的元素數量是陣列中顯式初始化元素的數量。未知大小陣列不能用 {} 初始化。
int x[] = {1, 3, 5}; // x has 3 elements struct Y { int i, j, k; }; Y y[] = {1, 2, 3, 4, 5, 6}; // y has only 2 elements: // 1, 2 and 3 appertain to y[0], // 4, 5 and 6 appertain to y[1] int z[] = {} // Error: cannot declare an array without any element
指定初始化器語法形式 (3,4) 被稱為指定初始化器:每個 designator 必須命名 T 的直接非靜態資料成員,並且表示式中使用的所有 designator 必須與 T 的資料成員以相同的順序出現。 struct A { int x; int y; int z; }; A a{.y = 2, .x = 1}; // error; designator order does not match declaration order A b{.x = 1, .z = 2}; // ok, b.y initialized to 0 由指定初始化器命名的每個直接非靜態資料成員都從指定符後面的相應花括號或等號初始化器初始化。禁止窄化轉換。 指定初始化器可用於將聯合體初始化為非第一個成員的狀態。聯合體只能提供一個初始化器。 union u { int a; const char* b; }; u f = {.b = "asdf"}; // OK, active member of the union is b u g = {.a = 1, .b = "asdf"}; // Error, only one initializer may be provided 對於非聯合體聚合體,未提供指定初始化器的元素將按上述方式初始化,即當初始化子句的數量少於成員數量時(如果提供了,則為預設成員初始化器,否則為空列表初始化) struct A { string str; int n = 42; int m = -1; }; A{.m = 21} // Initializes str with {}, which calls the default constructor // then initializes n with = 42 // then initializes m with = 21 如果使用指定初始化子句初始化的聚合體包含匿名聯合成員,則相應的指定初始化器必須命名該匿名聯合的一個成員。 注意:亂序指定初始化、巢狀指定初始化、混合指定初始化器和常規初始化器,以及陣列的指定初始化都在 C 程式語言中受支援,但在 C++ 中不允許。 struct A { int x, y; }; struct B { struct A a; }; struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ (out of order) int arr[3] = {[1] = 5}; // valid C, invalid C++ (array) struct B b = {.a.x = 0}; // valid C, invalid C++ (nested) struct A a = {.x = 1, 2}; // valid C, invalid C++ (mixed) |
(C++20 起) |
[編輯] 字元陣列
普通字元型別(char、signed char、unsigned char)、char8_t(C++20 起)、char16_t、char32_t(C++11 起) 或 wchar_t 的陣列可以分別用普通字串字面量、UTF-8 字串字面量(C++20 起)、UTF-16 字串字面量、UTF-32 字串字面量(C++11 起) 或寬字串字面量初始化,可選地用花括號括起來。此外,char 或 unsigned char 陣列可以用 UTF-8 字串字面量初始化,可選地用花括號括起來(C++20 起)。字串字面量的連續字元(包括隱式終止空字元)初始化陣列的元素,如果源值和目標值需要,則進行整型轉換(C++20 起)。如果指定了陣列的大小並且它大於字串字面量的字元數,則其餘字元被零初始化。
char a[] = "abc"; // equivalent to char a[4] = {'a', 'b', 'c', '\0'}; // unsigned char b[3] = "abc"; // Error: initializer string too long unsigned char b[5]{"abc"}; // equivalent to unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'}; wchar_t c[] = {L"кошка"}; // optional braces // equivalent to wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};
[編輯] 注意
聚合類或陣列可以包含非聚合公共基類(C++17 起)、成員或元素,它們按上述方式初始化(例如,從相應的初始化子句進行復制初始化)。
在 C++11 之前,聚合初始化中允許窄化轉換,但現在不再允許。
在 C++11 之前,聚合初始化只能用於變數定義,不能用於建構函式初始化列表、new-expression 或臨時物件建立,由於語法限制。
在 C 語言中,大小比字串字面量小一的字元陣列可以用字串字面量初始化;結果陣列不是空終止的。這在 C++ 中不允許。
功能測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__cpp_aggregate_bases |
201603L |
(C++17) | 帶有基類的聚合類 |
__cpp_aggregate_nsdmi |
201304L |
(C++14) | 帶有預設成員初始化器的聚合類 |
__cpp_aggregate_paren_init |
201902L |
(C++20) | 直接初始化形式的聚合初始化 |
__cpp_char8_t |
202207L |
(C++23) (DR20) |
char8_t 相容性和可移植性修復(允許從UTF-8 字串字面量初始化 (unsigned char 陣列) |
__cpp_designated_initializers |
201707L |
(C++20) | 指定初始化器 |
[編輯] 示例
#include <array> #include <cstdio> #include <string> struct S { int x; struct Foo { int i; int j; int a[3]; } b; }; int main() { S s1 = {1, {2, 3, {4, 5, 6}}}; S s2 = {1, 2, 3, 4, 5, 6}; // same, but with brace elision S s3{1, {2, 3, {4, 5, 6}}}; // same, using direct-list-initialization syntax S s4{1, 2, 3, 4, 5, 6}; // error until CWG 1270: // brace elision only allowed with equals sign int ar[] = {1, 2, 3}; // ar is int[3] // char cr[3] = {'a', 'b', 'c', 'd'}; // too many initializer clauses char cr[3] = {'a'}; // array initialized as {'a', '\0', '\0'} int ar2d1[2][2] = {{1, 2}, {3, 4}}; // fully-braced 2D array: {1, 2} // {3, 4} int ar2d2[2][2] = {1, 2, 3, 4}; // brace elision: {1, 2} // {3, 4} int ar2d3[2][2] = {{1}, {2}}; // only first column: {1, 0} // {2, 0} std::array<int, 3> std_ar2{{1, 2, 3}}; // std::array is an aggregate std::array<int, 3> std_ar1 = {1, 2, 3}; // brace-elision okay // int ai[] = {1, 2.0}; // narrowing conversion from double to int: // error in C++11, okay in C++03 std::string ars[] = {std::string("one"), // copy-initialization "two", // conversion, then copy-initialization {'t', 'h', 'r', 'e', 'e'}}; // list-initialization union U { int a; const char* b; }; U u1 = {1}; // OK, first member of the union // U u2 = {0, "asdf"}; // error: too many initializers for union // U u3 = {"asdf"}; // error: invalid conversion to int [](...) { std::puts("Garbage collecting unused variables... Done."); } ( s1, s2, s3, s4, ar, cr, ar2d1, ar2d2, ar2d3, std_ar2, std_ar1, u1 ); } // aggregate struct base1 { int b1, b2 = 42; }; // non-aggregate struct base2 { base2() : b3(42) {} int b3; }; // aggregate in C++17 struct derived : base1, base2 { int d; }; derived d1{{1, 2}, {}, 4}; // d1.b1 = 1, d1.b2 = 2, d1.b3 = 42, d1.d = 4 derived d2{{}, {}, 4}; // d2.b1 = 0, d2.b2 = 42, d2.b3 = 42, d2.d = 4
輸出
Garbage collecting unused variables... Done.
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 413 | C++98 | 匿名位欄位在聚合初始化中被初始化 | 它們被忽略 |
CWG 737 | C++98 | 當字元陣列用字串字面量初始化時 字元數少於陣列大小,在末尾 '\0' 之後的字元 元素未初始化 |
它們是 零初始化 |
CWG 1270 | C++11 | 花括號省略僅允許在複製列表初始化中使用 | 在其他地方允許 |
CWG 1518 | C++11 | 聲明瞭顯式預設建構函式或 具有繼承建構函式的類可以是聚合體 |
它不是一個 聚合體 |
CWG 1622 | C++98 | 聯合體不能用 {} 初始化 | 允許 |
CWG 2149 (P3106R1) |
C++98 | 不清楚花括號省略是否 適用於陣列大小推斷 |
適用 |
CWG 2272 | C++98 | 未顯式初始化的非靜態引用成員 從空初始化列表進行復制初始化 |
程式格式錯誤 在這種情況下,列舉是病態的 |
CWG 2610 | C++17 | 聚合型別不能有私有或保護的間接基類 | 允許 |
CWG 2619 | C++20 | 從指定初始化器進行的初始化種類不明確 | 它取決於 初始化器的種類 |
P2513R4 | C++20 | UTF-8 字串字面量不能初始化 char 陣列 或 unsigned char 陣列,這與 C 或 C++17 不相容 |
這種初始化 是有效的 |
[編輯] 參見
C 文件 關於 結構體和聯合體初始化
|