C++ 初始化中的求值顺序

C++ 初始化中的求值顺序,c++,initialization,language-lawyer,aggregate-initialization,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

在以下程序中:

#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;
}
从3.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 
在3.7版本中也使用了叮当声

在我自己的ubuntu上,gcc 6.2在构造
intAA[3][3]={aa[2][2]=1}
时出现了内部编译器错误

我假设这是未定义的行为,但在标准中找不到明确的陈述

问题是:

初始值设定项列表(例如
a[2]=1)中赋值的副作用评估顺序是否与标准中定义的数组实际元素(例如
a[2]
)的初始化顺序相同

是否明确规定为已定义或未定义?还是仅仅因为它没有明确定义就没有定义


或者,除了计算顺序之外,构造是否由于其他原因而具有已定义或未定义的行为?

让我们从最简单的情况开始:

这两个都是UB,因为违反了[basic.life]。在对象的生存期开始之前,您正在访问对象的值<代码>I
没有一个简单的默认构造函数,因此无法进行真空初始化。因此,对象的生命周期只有在构造函数完成后才开始。
A
数组的元素在访问该数组的元素时尚未构造

因此,您通过访问尚未构造的对象来调用UB

现在,其他两种情况更为复杂:

参见,
int
允许[basic.life]/1定义的“真空初始化”。已获取
a
aa
的存储。因此,
int a[3]
int
对象的有效数组,即使聚合初始化尚未开始。因此,访问对象,甚至设置其状态都是不必要的

这里的操作顺序是固定的。即使在C++17之前,初始化器列表元素的初始化也是在调用聚合初始化之前排序的,如[dcl.init.list]/4中所述。聚合中未在这里的初始化列表中列出的元素将像通过
typename{}
构造一样填充
int{}
表示初始化一个
int
值,结果为0

因此,即使您设置了
a[2]
aa[2][2]
,它们也应该通过聚合初始化立即被覆盖

因此,所有这些编译器都是错误的。答案应该是:


当然,这一切都很愚蠢,你不应该这么做。但从纯语言的角度来看,这是一种定义明确的行为。

我的眼睛在流血。。。如果这不是一次投票,我不知道会是什么。。。。如果你的代码乱七八糟,导致编译器的解析器崩溃,也会导致内部编译器错误。。。gcc 5+有一个小错误:在对象的生命周期开始之前使用对象有一个特殊的规定,这导致您可以在对象的构造函数启动(而不是完成)后立即访问对象成员。
0 0 1 
1 0 0 0 0 0 0 0 1 
1 2 2 
1 2 2 2 2 2 2 2 2 
I A[3] = {A[2].i = 1};
I AA[3][3] = {AA[2][2].i = 1};
int a[3] = {a[2] = 1};
int aa[3][3] = {aa[2][2] = 1};
1 0 0 
1 0 0 0 0 0 0 0 0