Подтвердить что ты не робот

Связывание ссылки на объект перед построением

Хорошо ли определено поведение следующего кода?

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;
}

На самом деле это было первоначальное вдохновение для этого вопроса, см. Циркулярная зависимость в списке инициализации конструктора

4b9b3361

Ответ 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 можно ожидать, когда этот международный стандарт опускает любое явное определение поведения или когда программа использует ошибочную конструкцию или ошибочные данные.