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

Is this C++ member initialization behavior well defined?

假设我们有一个 class B,它有一个 member,默认初始化为 42。这个 class 知道如何打印其 member 的值(它在构造函数中这样做):

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

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

  int member;
};

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

struct A
{
  A(const B& b) { b.printMember(); }
};

最后我们添加另一个 class Aggregate,它聚合了一个 A 和一个 B。棘手的部分是类型 A 的对象 a 在对象 b 类型 B 之前声明,但随后 a 使用(尚未生效? ) 参考 b:

struct Aggregate
{
  A a;
  B b;

  Aggregate() : a(b) { }
};

考虑创建 Aggregate 的输出(我在 AB 的构造函数和析构函数中添加了一些日志记录)(Try it online!):

a c'tor
value: 0
b c'tor
value: 42
b d'tor
a d'tor

我是否可以假设使用 b 的(尚未有效的)实例的引用来初始化 a 是无效的,因此这是未定义的行为?


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


帮我理解了需要区分

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

class 成员的构建按照他们在 class 中出现的顺序进行。虽然 B 的地址不会改变,而且从技术上讲你 ,正如@StoryTeller 指出的那样,在构造函数中调用 b.printMember() 和尚未构造的 b然而绝对是UB。

class个成员的初始化顺序如下。

来自 CPP 标准 (N4713),突出显示相关部分:

15.6.2 Initializing bases and members [class.base.init] ...
13 In a non-delegating constructor, initialization proceeds in the following order:
(13.1) — First, and only for the constructor of the most derived class (6.6.2), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
(13.2) — Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
(13.3) — Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
(13.4) — Finally, the compound-statement of the constructor body is executed.
[ Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. —end note ]