初始化中的评估顺序(Evaluation order in initialization)

系统教程 行业动态 更新时间:2024-06-14 16:59:47
初始化中的评估顺序(Evaluation order in 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] )的初始化?

它是明确规定的还是未定义的? 还是仅仅因为它没有明确定义而变得未定义呢?

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

In the following program:

#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; }

The output is

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

from http://ideone.com/1ueWdK with clang3.7

but the result is :

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

on http://rextester.com/l/cpp_online_compiler_clang also with clang 3.7.

On my own ubuntu, gcc 6.2 givs an internal compiler error on the construct int aa[3][3] = {aa[2][2] = 1}.

I'm assuming this is undefined behavior, but cannot find a definitive statement in the standard.

The question is:

Whether the evaluation order of the side effects on the assignment in the initializer list (e.g. a[2] = 1) and initialization of the actual element of the array (e.g. a[2]) defined in the standard?

It is explicitly stated as defined or undefined? Or does it become undefined just because it is not explicitly defined?

Or does the construct has defined or undefined behavior due to other reason aside from the evaluation order?

最满意答案

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

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

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

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

现在,另外两种情况更为复杂:

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。

这里的操作顺序是固定的。 即使在pre-C ++ 17中,在调用聚合初始化之前,初始化程序列表元素的初始化也是按顺序排列的,如[dcl.init.list] / 4中所述。 在这里初始化列表中未列出的聚合中的元素将按照typename{}构造来填充。 int{}表示对值进行初始化,结果为0。

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

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

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

现在批准了,这一切都很愚蠢,你不应该这样做。 但从纯粹的语言角度来看,这是明确定义的行为。

Let's start with the simplest case:

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

Both of these are UB, due to a violation of [basic.life]. You are accessing the value of an object before its lifetime has begun. I does not have a trivial default constructor, and therefore cannot be vacuously initialized. Therefore, the object's lifetime only begins once a constructor has completed. The elements of the A array have not yet been constructed when you are accessing elements of that array.

Therefore, you are invoking UB by accessing a not-yet-constructed object.

Now, the other two cases are more complex:

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

See, int permits "vacuous initialization", as defined by [basic.life]/1. Storage for a and aa has been acquired. Therefore, int a[3] is a valid array of int objects, even though aggregate initialization has not yet begun. So accessing the object and even setting its state is not UB.

The order of operations here is fixed. Even pre-C++17, the initialization of the elements of the initializer list is sequenced before the aggregate initialization is invoked, as stated in [dcl.init.list]/4. Elements in the aggregate which are not listed in the initialization list here will be filled in as if by typename{} constructs. int{} means to value-initialize an int, which results in 0.

So even though you set a[2] and aa[2][2], they should immediately be overwritten via aggregate initialization.

Therefore, all of these compilers are wrong. The answer should be:

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

Now granted, this is all very stupid and you shouldn't do it. But from a pure language perspective, this is well-defined behavior.

更多推荐

本文发布于:2023-04-17 08:57:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/dzcp/0b1b29887c7ce0136f0c9499e4f71781.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:初始化   顺序   Evaluation   order   initialization

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!