Является ли поведение undefined ссылкой на членов класса в списке инициализации?

Скажем, у меня есть следующее:

class A {
   B member1;
   C member2;
public:
   A();
};

class B {
public:
   C& ref_to_c;
   B( C& ref_to_c );
};

class C {
 ...
};

B требует, чтобы ссылка на C была предоставлена ​​на его конструкторе. Если класс A предоставляет C, является ли законным указывать список инициализаторов как следующий...

A() : member1( B( member2 ) ) {}

То есть, член2 существует в фазе списка инициализации или это поведение undefined?

Ответ 1

Интериализация выглядит следующим образом:

5 Инициализация должна выполняться в следующем порядке:

- Во-первых, и только для конструктора самого производного класса как описанных ниже, виртуальные базовые классы должны быть инициализированы в порядок, который они появляются на первом переходе слева направо на глубину направленный ациклический граф базовых классов, где "слева направо" является порядок появления имен базового класса в производном классе базовый спецификатор-лист.

- Затем прямые базовые классы должны быть инициализированы в порядке объявления поскольку они появляются в списке-спецификаторе-базе (независимо от порядка mem-инициализаторы).

- Затем нестатические члены данных должны быть инициализированы в порядке они были объявлены в определении класса (снова независимо от порядок mem-инициализаторов).

- Наконец, выполняется тело конструктора. [Обратите внимание заказ на декларацию уполномочен обеспечить, чтобы база и член подобъекты уничтожаются в обратном порядке инициализации. ]


Что в основном означает, что member1 всегда будет инициализироваться до member2. Таким образом, конструктор B будет работать первым. Даже если вы назовете их в обратном порядке в конструкторе A:

A() : member2(foo), member1(bar) {}

это не имеет значения. Теперь ссылка на неинициализированный объект не является UB сама по себе, но она может зависеть от конструктора B. Вы должны переключить порядок объявления:

C member2;
B member1;

Ответ 2

Вы составляете member1, который содержит ссылку на memebr2. Это еще не построено, но компилятор уже знает, где он будет (поэтому он может предоставить ссылку).

Он будет работать, но будет UB, если вы попробуете - например, в B ctor- - получить доступ к значению ref_to_c в каком-то выражении, так как ссылка фактически накладывает неинициализированную память, которая будет инициализирована во время member2 строительство, которое произойдет позже.

Та же проблема будет в деструкторе B, где member2 будет уничтожен до ref_to_c.

Лучше, если вы замените member2 и member1 на A, чтобы вы инициализировали ссылку с помощью сконструированного объекта, делая каждое возможное использование определенным.