【发布时间】:2016-01-01 15:38:48
【问题描述】:
考虑以下几点:
struct mystruct
{
int i;
int j;
};
int main(int argc, char* argv[])
{
mystruct foo{45, foo.i};
std::cout << foo.i << ", " << foo.j << std::endl;
return 0;
}
注意在聚合初始化器列表中使用foo.i。
g++ 5.2.0 输出
45、45
这是定义明确的行为吗?这个聚合初始化器中的foo.i 是否总是保证引用正在创建的结构的i 元素(例如,&foo.i 将引用该内存地址)?
如果我向mystruct 添加显式构造函数:
mystruct(int i, int j) : i(i), j(j) { }
然后我收到以下警告:
main.cpp:15:20: warning: 'foo.a::i' is used uninitialized in this function [-Wuninitialized]
a foo{45, foo.i};
^
main.cpp:19:34: warning: 'foo.a::i' is used uninitialized in this function [-Wuninitialized]
cout << foo.i << ", " << foo.j << endl;
代码编译,输出为:
45, 0
显然,这会有所不同,我假设这是未定义的行为。是吗?如果是这样,为什么 this 和没有构造函数时有区别?而且,如何使用用户定义的构造函数获得初始行为(如果它是明确定义的行为)?
【问题讨论】:
-
好问题。据我所知,列表初始化仅确保参数的评估顺序是从左到右的,并且很少有情况不会调用 UB,否则会调用 UB,例如:
mystruct x {++i, ++i};是明确定义的,尽管mystruct y(++i, ++i);是不是。仅此而已。由于您的代码没有被规范转换,因此我认为它调用了 UB。 -
由于省略而看起来像 UB(“[defns.undefined] 当本国际标准省略任何明确的行为定义时,可能会出现未定义的行为......”)标准说([dcl.init.list]/4) brace-init-list 中的各个初始值设定项按顺序进行评估,但没有说明如何实际将每个初始值设定项应用于聚合的相应成员与该订单进行交互。
-
@IgorTandetnik [dcl.init.aggr]/p7 中的示例会提出其他建议。
-
@johannes-schaub-litb 基于标准或关闭为重复的George Stocker lists here。基本上观点、投票和完整答案我认为应该是相反的。
-
鉴于 Johannes 指出的缺陷报告和标准讨论线程,我认为在缺陷报告得到澄清之前依赖第一个案例是不明智的。我相应地更新了我的答案。抄送@T.C.
标签: c++ c++11 language-lawyer c++14 undefined-behavior