Копирование пустого объекта связано с его доступом

Вдохновленный из этого вопроса.

struct E {};
E e;
E f(e);  // Accesses e?

К access относится к

чтение или изменение значения объекта

Пустой класс имеет неявно определенный конструктор копирования

Неявно определенный конструктор copy/move для неединичного класса X выполняет поэтапную копирование/перемещение своих баз и элементов. [...] Порядок инициализации совпадает с порядком инициализации баз и членов в определяемом пользователем конструкторе. Пусть X - либо параметр конструктора, либо для конструктора перемещения значение x, относящееся к параметру. Каждый базовый или нестатический элемент данных копируется/перемещается в соответствии с его типом:

  • [...] база или элемент напрямую инициализируются с соответствующей базой или элементом X.

Ответы

Ответ 1

Я думаю, что часть стандарта, которая описывает наиболее точное, что выполняет доступ, - [basic.life]. В этом параграфе объясняется, что можно сделать с ссылкой, которая ссылается, или указателем, указывающим на объект, который не входит в его жизненный период. Все, что разрешено делать с такими объектами, не выполняет доступ к значению объекта, поскольку такое значение не существует (иначе стандарт был бы непоследовательным).

Таким образом, мы можем принять более резкий пример, если это не поведение undefined, поэтому в вашем примере кода нет доступа к e (в соответствии с вышеизложенным рассуждением):

struct E{
     E()=default;
     E(const E&){}
     };
E e;
e.~E();
E f(e);

Здесь e - объект, срок жизни которого закончился, но хранилище которого все еще выделено. Что можно сделать с такой lvalue, описано в [basic.life]/6

Аналогично, до того, как началось время жизни объекта, но после того, как хранилище, которое будет занимать объект, было выделено или, после того, как срок жизни объекта закончился и перед хранилищем, которое объект занят, повторно используется или выпущен, любое значение glvalue который относится к исходному объекту, но может использоваться только ограниченным образом. Строку или разрушение объекта см. В разделе [class.cdtor]. В противном случае такое значение glvalue относится к выделенному хранилищу ([basic.stc.dynamic.deallocation]) и , используя свойства glvalue, которые не зависят от его значения, четко определено. Программа имеет undefined поведение, если:

  • преобразование lvalue-to-rvalue ([conv.lval]) применяется к такому glvalue,

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

  • glvalue неявно преобразуется ([conv.ptr]) в ссылку на тип базового класса или

  • glvalue используется как операнд static_cast ([expr.static.cast]), за исключением случаев, когда преобразование в конечном итоге соответствует cv char & или cv без знака char &, или

  • glvalue используется как операнд dynamic_cast ([expr.dynamic.cast]) или как операнд typeid.

Ни один из приведенных выше пунктов не выполняется внутри конструктора e copy, поэтому код примера в этом ответе четко определен, что подразумевает отсутствие доступа к значению разрушенного объекта. Таким образом, в вашем примере кода нет доступа к e.

Ответ 2

Я думаю, что он не обращается к объекту, хотя должен присутствовать действительный объект.

E f(e);

Это вызывает E неявно определенный конструктор E::E(const E&). Очевидно, что тело этого конструктора пуст (потому что нечего делать). Поэтому, если что-то произойдет, это должно произойти во время передачи аргумента, то есть во время инициализации const E& из E.

Само собой разумеется, что эта инициализация не изменяет E. Теперь, чтобы прочитать значение E, должно произойти преобразование lvalue-to-rvalue. Тем не менее, стандарт фактически говорит, что это преобразование не происходит во время прямого привязки ссылки 1. То есть, чтение не выполняется.

Однако стандарт требует, чтобы ссылка была инициализирована для ссылки на действительный объект или функцию 2 (хотя это подлежит CWG 453), поэтому такие вещи, как E f(*reinterpret_cast<E*>(nullptr));, будут плохо сформированы.


1. Это делается не нормативно, требуя такого преобразования, и еще более упрощается ненормативная нота в [dcl.init.ref].

2. [dcl.ref].