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

Класс, содержащий ссылку на себя

Снимая стандартную черновик (n3242), я нашел это предложение в разделе 9.2 (выделение мое):

Нестатические (9.4) элементы данных не должны иметь неполных типов. В в частности, класс C не должен содержать нестатический член класса C, но он может содержать указатель или ссылку для объекта класса С.

Из этого я утверждаю, что это нормально, чтобы определить такой класс:

class A {
public:
  A(A& a) : a_(a){
  }
private:
  A& a_;
};

Затем в разделе 8.3.2 я нашел следующее:

Ссылка должна быть инициализирована для ссылки на действительный объект или Функция

Вопрос 1: Разрешено ли определять объект этого типа, передавая его имя в качестве ссылки:

A a(a);

или это приведет к срабатыванию undefined поведения?

Вопрос 2: Если да, то каковы части стандарта, которые позволяют инициализировать ссылку из объекта, который все еще будет построен?

Вопрос 3: Если нет, значит ли это, что определение класса A хорошо сформировано, но не может быть создан первый объект без запуска UB? В этом случае, в чем причина этого?

4b9b3361

Ответ 1

"действительный объект" не определен нигде в стандарте, но он имеет намерение означать область памяти с соответствующим размером и выравниванием, которая может содержать объект указанного типа. Это просто означает исключить ссылки на такие вещи, как разыменованные нулевые указатели, несогласованные области памяти и т.д. Действителен неинициализированный объект.

Существует открытая проблема, чтобы прояснить формулировку, CWG 453.

Ответ 2

n3337 § 3.8/6

Аналогично, до начала жизни объекта, но после хранилище, которое будет занимать объект, или, после срок службы объекта закончился и перед хранилищем, которое объект, занятый, повторно используется или освобождается, любое значение gl, которое ссылается на оригинальный объект может использоваться, но только ограниченным образом. Для объекта под строительство или уничтожение, см. 12.7. В противном случае такое значение glvalue относится к выделенному хранилищу (3.7.4.2) и использует свойства glvalue, которые не зависят от его значения, четко определены. Программа имеет поведение undefined, если:

- преобразование lvalue-to-rvalue (4.1) является применяется к такому glvalue,

- значение glvalue используется для доступа к нестатический элемент данных или вызов нестатической функции-члена объект или

- glvalue неявно преобразуется (4.10) в ссылку к типу базового класса или

- значение gl используется как операнд static_cast (5.2.9), за исключением случаев, когда конверсия в конечном счете равна cv char & или cv без знака char &, или

- значение gl используется как операнд dynamic_cast (5.2.7) или как операнд typeid.

Итак, чтобы ответить на ваши вопросы:

Вопрос 1: Разрешено ли определение объекта такого типа его имя в качестве ссылки?

Да. Использование только адреса, похоже, не нарушает его (по крайней мере, для переменной put on stack).

A a(a);

или это приведет к срабатыванию undefined поведения?

Нет.

Вопрос 2: Если да, каковы части стандарта, которые разрешают инициализация ссылки из неподвижного объекта?

§ 3.8/6 (выше)


Остается только вопрос, как это соотносится с

Ссылка должна быть инициализирована для ссылки на действительный объект или функция.

Проблема заключается в терминальном действительном объекте. Поскольку § 8.3.2/4 говорит, что

Не указано, нужна ли ссылка для хранения

кажется, что § 8.3.2 является проблематичным и его следует переформулировать. Путаница приводит к предложению в документе "Активные проблемы с стандартным ядром основного языка С++", редакция 87 от 20.01.2014:

Ссылка должна быть инициализирована для ссылки на объект или функцию.

Измените 8.3.2 [dcl.ref] пункт 4 следующим образом:

Если значение l, к которому ссылка напрямую привязана, не обозначает существующий объект или функцию соответствующего типа (8.5.3 [dcl.init.ref]), а также область памяти подходящего размера и выравнивания содержать объект ссылочного типа (1.8 [intro.object], 3.8 [basic.life], 3.9 [basic.types]), поведение undefined.

Ответ 3

Из n1905, 3.3.1.1

Точка объявления для имени сразу после завершения (пункт 8) и перед его инициализатором (если есть), кроме как отмечается ниже.

[Пример:
int x = 12;
{int x = x; }

Здесь второй х инициализируется его собственным (неопределенным) значением.

-end пример]

Мой акцент (исправьте меня, если я ошибаюсь): В вашем примере -

A a(a);

эквивалентно -

A a = a;  // Copy initialization

Таким образом, согласно стандарту a инициализируется его собственное неопределенное значение. И элемент удерживает ссылку на одно такое неопределенное значение.