Связывание ссылки на объект перед построением
Хорошо ли определено поведение следующего кода?
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;
}
На самом деле это было первоначальное вдохновение для этого вопроса, см. Циркулярная зависимость в списке инициализации конструктора
Ответы
Ответ 1
Поведение # 2 определенно четко определено. Как упоминалось @dyp, соответствующий параграф находится в [basic.life]:
![введите описание изображения здесь]()
Связывание glvalue yobj
с эталонными точками, поскольку его хранение длится в течение всего времени программы ([basic.stc.static]/1), и ссылка привязана к действительной стороне объекта - в которой отвечает требованиям ([dcl.ref]/5). Аналогично, для второго примера, который вы указали, до тех пор, пока не выполняется никакая операция над членами подобъекта A
, вышеприведенный параграф применяется также, поскольку конструктор C
вызывается в выделенном хранилище this
.
Ответ 2
[...] нет упоминания ссылок в примере [...]
Вы имеете в виду, кроме
[...] ссылается на любой нестатический член [...]
Из прохода, который вы цитируете, я бы сказал, что эта строка вызывает поведение undefined:
int& r3 = yobj.i; // #3
потому что вы:
[...] ссылаясь на любой нестатический член или базовый класс объекта до того, как конструктор начинает выполнение [.]
Кроме того, для этого:
и самое главное, приведенный выше параграф не означает, что в противном случае поведение корректно определено.
Вы правы, это не так:
Undefined можно ожидать, когда этот международный стандарт опускает любое явное определение поведения или когда программа использует ошибочную конструкцию или ошибочные данные.