这个C++成员初始化行为是否定义良好?

sim*_*mon 21 c++ constructor initialization default-constructor undefined-behavior

假设我们有一个类B,有一个member是默认初始化42.这个类知道如何打印它的值member.(它在c'tor中这样做):

struct B
{
  B() : member(42) { printMember(); }

  void printMember() const { std::cout << "value: " << member << std::endl; }

  int member;
};
Run Code Online (Sandbox Code Playgroud)

然后我们添加一个A接收a的const引用的类,B并要求B打印它的值:

struct A
{
  A(const B& b) { b.printMember(); }
};
Run Code Online (Sandbox Code Playgroud)

最后,我们添加另一个Aggregate聚合a A和a的类B.棘手的部分是a类型的对象A在对象b类型之前声明B,但是然后a使用(尚未生效的?)引用初始化b:

struct Aggregate
{
  A a;
  B b;

  Aggregate() : a(b) { }
};
Run Code Online (Sandbox Code Playgroud)

考虑创建一个的输出Aggregate(我已经添加了一些日志记录到c'tor和d'tor AB)(在网上尝试!):

a c'tor
value: 0
b c'tor
value: 42
b d'tor
a d'tor
Run Code Online (Sandbox Code Playgroud)

我是否正确地认为a使用对(尚未生效的)实例的引用进行初始化是无效的,b因此这是未定义的行为?


编辑:我知道初始化顺序.这就是让我挣扎的原因.我知道b还没有构建,但我也知道b未来的地址甚至可以在b构建之前确定.因此我假设可能有一些我不知道的规则允许编译器bb构造之前默认初始化s成员或类似的东西.(如果第一个打印出来的值看起来是随机的而不是0(默认值int),那就更明显了)


编辑:这个答案帮助我理解我需要区分

  • 绑定对未初始化对象的引用(有效)和
  • 通过引用访问未初始化的对象(未定义)

Dev*_*ull 23

是的,你是对的,它是UB,但出于不同的原因而不仅仅是存储对尚未构造的对象的引用.

班级成员的建构按照他们在班级中出现的顺序进行.虽然地址B不会改变,但从技术上来说你可以存储对它的引用,正如@StoryTeller指出的那样,b.printMember()在构造函数中调用b尚未构造的那个肯定是UB.