C++14 中的聚合成员初始化
Aggregate Member Initialization in C++14
具有这种结构:
struct A {
struct B {
int a = 21;
int b;
int c = 22;
int d;
int e = 23;
};
B b1 = { 11, 12 };
B b2 = { 11, 12, 13 };
int x;
};
并声明:
A a = { { 1, 2, 3, 4 }, { 1 }, 5 };
根据 Clang (3.8.0) 和 GCC (5.4.0),这些是 8 种可能组合的值(a.b1.e 和 a.b2.a 是重复的情况),关于初始值取自(或不取自)的位置,:
a.b1.a = 1 // 111
a.b1.b = 2 // 110
a.b1.c = 3 // 101
a.b1.d = 4 // 100
a.b2.b = 0 // 010 // Why not 12 instead of 0? -> Explained in N3605
a.b2.c = 22 // 011 // Why not 0 instead of 22 ? Why not 13 ?
a.b2.d = 0 // 000
a.b2.e = 23 // 001 // Why not 0 instead of 23 ?
考虑到 N3605 和 C++14 标准 (ISO/IEC 14882:2014) 中的示例,第 8.5.1 节,第 7 段:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from its brace-or-equal-initializer or, if there is no brace-or-equal-initializer, from an empty initializer list (8.5.4).
我假设案例 010 是正确的。那么,为什么情况 011 (a.b2.c) 和 001 (a.b2.e) 不也等于零?案例 010 为零,因为 a.b2 "does have an initializer",因此 "the non-static data member initializer is ignored"(又是 N3605)。为什么不忽略默认成员初始值设定项?
事实上,阅读 C++14 标准引述对我来说更有意义的是 case 010 是 12(它是零),case 011 和 001 是零(因为它们实际上是)。所以我不明白的是为什么 a.b2 有时被认为是 "have an initializer" 而其他时候不是。
在此示例中,{1, 2, 3, 4}
是 B b1
的初始值设定项。编译器已经有一个初始化器,所以现在它不会再看 { 11, 12 }
。
您声明 a
并为其所有成员设置初始值设定项:b1
、b2
和 x
。这意味着我们构造为 if
a.b1 = B{ 1, 2, 3, 4 };
a.b2 = B{ 1 };
a.x = 5;
B
的定义是 B{ 1, 2, 3, 4 }
表示 B{ 1, 2, 3, 4, 23 }
,B{ 1 }
表示 B{ 1, 0, 22, 0, 23 }
。这正是您得到的结果。
如果你写了
A a = { { 1, 2, 3, 4 }, };
然后 a.b2
将使用其默认值 { 11, 12 }:
进行初始化
a.b1 = B{ 1, 2, 3, 4 };
a.b2 = B{ 11, 12 };
a.x = {};
将您的示例中的 { 1 }
和 { 11, 12 }
等大括号表达式视为完全构造的 B
对象可能会有所帮助,远早于 A
的构造函数甚至看到它们。
具有这种结构:
struct A {
struct B {
int a = 21;
int b;
int c = 22;
int d;
int e = 23;
};
B b1 = { 11, 12 };
B b2 = { 11, 12, 13 };
int x;
};
并声明:
A a = { { 1, 2, 3, 4 }, { 1 }, 5 };
根据 Clang (3.8.0) 和 GCC (5.4.0),这些是 8 种可能组合的值(a.b1.e 和 a.b2.a 是重复的情况),关于初始值取自(或不取自)的位置,:
a.b1.a = 1 // 111
a.b1.b = 2 // 110
a.b1.c = 3 // 101
a.b1.d = 4 // 100
a.b2.b = 0 // 010 // Why not 12 instead of 0? -> Explained in N3605
a.b2.c = 22 // 011 // Why not 0 instead of 22 ? Why not 13 ?
a.b2.d = 0 // 000
a.b2.e = 23 // 001 // Why not 0 instead of 23 ?
考虑到 N3605 和 C++14 标准 (ISO/IEC 14882:2014) 中的示例,第 8.5.1 节,第 7 段:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from its brace-or-equal-initializer or, if there is no brace-or-equal-initializer, from an empty initializer list (8.5.4).
我假设案例 010 是正确的。那么,为什么情况 011 (a.b2.c) 和 001 (a.b2.e) 不也等于零?案例 010 为零,因为 a.b2 "does have an initializer",因此 "the non-static data member initializer is ignored"(又是 N3605)。为什么不忽略默认成员初始值设定项?
事实上,阅读 C++14 标准引述对我来说更有意义的是 case 010 是 12(它是零),case 011 和 001 是零(因为它们实际上是)。所以我不明白的是为什么 a.b2 有时被认为是 "have an initializer" 而其他时候不是。
在此示例中,{1, 2, 3, 4}
是 B b1
的初始值设定项。编译器已经有一个初始化器,所以现在它不会再看 { 11, 12 }
。
您声明 a
并为其所有成员设置初始值设定项:b1
、b2
和 x
。这意味着我们构造为 if
a.b1 = B{ 1, 2, 3, 4 };
a.b2 = B{ 1 };
a.x = 5;
B
的定义是 B{ 1, 2, 3, 4 }
表示 B{ 1, 2, 3, 4, 23 }
,B{ 1 }
表示 B{ 1, 0, 22, 0, 23 }
。这正是您得到的结果。
如果你写了
A a = { { 1, 2, 3, 4 }, };
然后 a.b2
将使用其默认值 { 11, 12 }:
a.b1 = B{ 1, 2, 3, 4 };
a.b2 = B{ 11, 12 };
a.x = {};
将您的示例中的 { 1 }
和 { 11, 12 }
等大括号表达式视为完全构造的 B
对象可能会有所帮助,远早于 A
的构造函数甚至看到它们。