Хорошо ли определено поведение следующего кода?
struct X { int i; }; // trivial
struct Y : X { Y(){} }; // non-trivial
extern X xobj;
int& r1 = xobj.i; // #1
X xobj;
extern Y yobj;
Y& r2 = yobj; // #2
// int& r3 = yobj.i; // #3 - this is UB according to the standard
Y yobj;
Этот код вдохновлен примером в стандарте С++, а именно черновик N4140 [class.cdtor]/1.
Что говорится в этом абзаце:
Для объекта с нетривиальным конструктором, ссылающегося на любой нестатический член или базовый класс объекта перед тем, как конструктор начнет выполнение, результат будет выглядеть как undefined. Для объекта с нетривиальным деструктор, ссылаясь на любой нестатический член или базовый класс объекта после завершения деструктора выполнение приводит к поведению undefined.
Далее следует пример, показывающий, как указатели могут и не могут быть привязаны к объектам.
Итак, интуитивно кажется, что #1
и #2
определены корректно, а #3
вызывает UB, если он не подписан, но, во-первых, примеры не являются нормативными, во-вторых, нет упоминания ссылок в примере и третий и самый важный, вышеупомянутый абзац не означает, что в противном случае поведение четко определено. Или это? Или, может быть, есть другая соответствующая цитата в стандарте, который я пропустил?
Изменить. Ответ может (возможно) быть да, если объекты имеют статическую продолжительность хранения, но они также могут быть локальными, например:
struct A { A(){} };
struct B { B(A&){} };
struct C {
B b;
A a;
C() : b(a) {}
};
int main() {
C c;
}
На самом деле это было первоначальное вдохновение для этого вопроса, см. Циркулярная зависимость в списке инициализации конструктора