fef*_*efe 5 c++ initialization language-lawyer aggregate-initialization
在以下程序中:
#include <iostream>
struct I {
int i;
I(){i=2;}
I(int _i){i=_i;}
};
int a[3] = {a[2] = 1};
int aa[3][3] = {aa[2][2] = 1};
I A[3] = {A[2].i = 1};
I AA[3][3] = {AA[2][2].i = 1};
int main(int argc, char **argv) {
for (int b : a) std::cout << b << ' ';
std::cout << '\n';
for (auto &bb : aa) for (auto &b : bb) std::cout << b << ' ';
std::cout << '\n';
for (auto &B : A) std::cout << B.i << ' ';
std::cout << '\n';
for (auto &BB : AA) for (auto &B : BB) std::cout << B.i << ' ';
std::cout << '\n';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出是
1 0 0
1 0 0 0 0 0 0 0 1
1 2 2
1 2 2 2 2 2 2 2 2
Run Code Online (Sandbox Code Playgroud)
来自 http://ideone.com/1ueWdK和clang3.7
但结果是:
0 0 1
1 0 0 0 0 0 0 0 1
1 2 2
1 2 2 2 2 2 2 2 2
Run Code Online (Sandbox Code Playgroud)
在http://rextester.com/l/cpp_online_compiler_clang上也有clang 3.7.
在我自己的ubuntu上,gcc 6.2在构造上给出了内部编译器错误int aa[3][3] = {aa[2][2] = 1}.
我假设这是未定义的行为,但无法在标准中找到明确的声明.
问题是:
是否对初始化列表中的赋值的评估顺序(例如a[2] = 1)和a[2]标准中定义的数组的实际元素(例如)的初始化?
明确说明是定义还是未定义?或者它是否因为没有明确定义而变得不确定?
或者,由于除评估顺序之外的其他原因,构造是否已定义或未定义行为?
让我们从最简单的情况开始:
Run Code Online (Sandbox Code Playgroud)I A[3] = {A[2].i = 1}; I AA[3][3] = {AA[2][2].i = 1};
由于违反了 [basic.life],这两个都是 UB。您正在访问对象的生命周期开始之前的值。I没有简单的默认构造函数,因此不能空初始化。因此,对象的生命周期仅在构造函数完成后才开始。当您访问该数组的元素时,该数组的元素A尚未构造。
因此,您通过访问尚未构造的对象来调用 UB。
现在,另外两种情况更加复杂:
Run Code Online (Sandbox Code Playgroud)int a[3] = {a[2] = 1}; int aa[3][3] = {aa[2][2] = 1};
请参阅,int允许“空初始化”,如 [basic.life]/1 所定义。存储a和aa已被获取。因此,int a[3]是一个有效的对象数组int,即使聚合初始化尚未开始。因此访问对象甚至设置其状态都不是UB。
这里的操作顺序是固定的。即使在 C++17 之前,初始化器列表的元素初始化也会在调用聚合初始化之前进行排序,如 [dcl.init.list]/4 中所述。聚合中未在初始化列表中列出的元素将像typename{}构造一样被填充。int{}表示对 an 进行值初始化int,结果为 0。
因此,即使您设置了a[2]和aa[2][2],它们也应该立即通过聚合初始化被覆盖。
因此,所有这些编译器都是错误的。答案应该是:
Run Code Online (Sandbox Code Playgroud)1 0 0 1 0 0 0 0 0 0 0 0
现在承认,这都是非常愚蠢的,你不应该这样做。但从纯粹的语言角度来看,这是明确定义的行为。