c++ - 初始化中的评估顺序

标签 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;
}

输出是

1 0 0 
1 0 0 0 0 0 0 0 1 
1 2 2 
1 2 2 2 2 2 2 2 2

来自 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 

关于 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]) 在标准中定义?

它被明确声明为已定义还是未定义?还是因为它没有明确定义而变成未定义?

或者由于评估顺序之外的其他原因,构造是否具有已定义或未定义的行为?

最佳答案

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

I A[3] = {A[2].i = 1};
I AA[3][3] = {AA[2][2].i = 1};

由于违反了 [basic.life],这两个都是 UB。您正在访问对象的生命周期开始之前的值。 I 没有平凡的默认构造函数,因此不能空洞地初始化。因此,对象的生命周期仅在构造函数完成后才开始。当您访问该数组的元素时,A 数组的元素尚未构建。

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

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

int a[3] = {a[2] = 1};
int aa[3][3] = {aa[2][2] = 1};

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

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

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

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

1 0 0 
1 0 0 0 0 0 0 0 0

现在承认,这一切都是非常愚蠢的,你不应该这样做。但从纯语言的角度来看,这是定义明确的行为。

关于c++ - 初始化中的评估顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40700249/

相关文章:

c++ - 返回对内部对象的引用时的常量性

c++ - recursive_mutex 的最大所有权级别的下限?

c++ - 一个 vector 在 2500 万个 vector 中的查找距离

c++ - 使用未初始化的成员复制结构

c - 将结构体的所有成员(相同的基本数据类型)初始化为一个给定值

C++测试矩阵是否已经初始化

c++ - 移位运算符、位运算符和 sizeof 运算符产生的结果的值类别是什么?

c++ - 我的 bst 实现有什么问题吗?

c++ - 为什么我不能将一个枚举作为另一个枚举的基础类型?

c++ - 如何在 gdb 输出中隐藏类型信息