C++ 正在以自己的初始值设定项未定义的行为访问结构的成员
考虑:C++ 正在以自己的初始值设定项未定义的行为访问结构的成员,c++,initialization,C++,Initialization,考虑: struct A { void do_something() {} }; struct B { A& a; B(A& a) : a{a} { a.do_something(); } }; struct C { A a; B b; C(A& a) : b{a} {} }; int main() { C c{ c.a }; } 这似乎是可行的,因为: 甚至在c初始化之前,我们就知道它的内存布局和c.a的地址 在初始化c.a之前,我们实际上不
struct A { void do_something() {} };
struct B {
A& a;
B(A& a) : a{a} { a.do_something(); }
};
struct C {
A a; B b;
C(A& a) : b{a} {}
};
int main() {
C c{ c.a };
}
这似乎是可行的,因为:
甚至在c初始化之前,我们就知道它的内存布局和c.a的地址
在初始化c.a之前,我们实际上不会使用它。
此外,我没有在几个不同的编译器下收到警告
然而,我后来经历了一些非常奇怪的行为,这只能归结为做了一些未定义的事情,只有当我重新组织我的程序以避免这种模式时,这些行为才会消失
谢谢。您的程序有未定义的行为,因为您正在访问对象外部的对象成员,并且在对象的生存期开始之前 对于具有非平凡构造函数的对象, 之前引用对象的任何非静态成员或基类 构造函数开始执行导致未定义的行为 至于为什么它编译得很好:一个名字可以在它的声明点之后使用。。。并引用C++标准草案…重点地雷: 名称的声明点是立即 在其完整的声明符子句之后和 如果有的话 定义为在此处部分复制语法: 初始化器 上述原因与此相同:
int k(k);
int m{m};
int b = b;
除了我先前的回答之外 你的代码很狡猾。。。因为,尽管假设UB在初始化对象之前使用对象,但代码的行为显然定义得很好 怎么做? 在c的构造过程中,将发生以下事件序列: 调用C的构造函数CA&a:b{a}{},它引用类型为a的对象。引用就像地址一样,正如您正确提到的,C.a的地址在编译时是已知的。你的电话是:C{C.a};编译器对此没有问题,因为c.a是一个可访问的名称 由于C成员的声明顺序
struct C {
A a; B b;
C(A& a) : b{a} {}
};
对象a在对象b之前初始化
因此,在成员初始值设定项中使用前,a将变为活动状态。。。b{a}
但同样,你也可能会被…所吸引。C++有一个非常明确的构造顺序。首先初始化非虚拟基类。接下来,将按照成员在类中的声明顺序初始化成员,忽略初始值设定项列表的顺序,并且只有在初始化最后一个成员之后,才会输入构造函数的主体 在本例中,这意味着按A::A、B::B、C::C的顺序调用这些CTOR 如果C被声明为
struct C {
B b; A a;
C(A& a) : b{a} {}
};
在c的生命周期开始之前,在c的构造函数之外访问c.a是一个问题。这有点像int x=x;。但是我的程序运行得很好@斯图,未定义的行为并不意味着代码无法工作,您当前的代码段运行良好,并且应该适用于每个sane编译器。。但UB也意味着编译器可以生成重新格式化PC的代码;;-我想,这是相关的。“波格丹,谢谢,这是我关心的答案。我不确定刚才描述C++源解析的语法是否与运行时相关。本节只是告诉我们在源文本中某个特定标记开始具有特定含义的时间点,但这是文本中的时间点而不是时间点。好的,[12.7p1]的问题仍然存在,这表示对于具有非平凡构造函数的对象,在构造函数开始执行之前引用对象的任何非静态成员或基类会导致未定义的行为。[...]. 但是,这可能会在将来进行调整。请看我对这个问题的评论。@bogdan:我注意到了,读了CWG的问题,对你投了赞成票,然后写下了答案。正如您可以从CWG问题中推断的那样,所有合理解释都允许形成参考。CWG问题中的合理解释是指[3.8]中“访问”一词的解释,同时在标准措辞中对此进行了澄清,因此我们同意该部分。我发表评论是因为我读了你的答案,意思是你很好,我认为只要[12.7p1]明确地使代码结果为UB in C{C.a};,这就不是一个安全的假设;,表达式c.a是在c的构造函数开始执行之前计算的。这是我的推理,但就像我说的,它在20行之后给了我奇怪的bug,直到我修复了它。
struct C {
B b; A a;
C(A& a) : b{a} {}
};