Правильно ли определено получение адреса члена неинициализированного объекта?

Рассмотрим следующий пример. Когда bar создан, он дает конструктору базового типа (foo) адрес my_member.y, где my_member — элемент данных, который еще не был инициализирован.

struct foo {
    foo(int * p_x) : x(p_x) {}
    int * x;
};

struct member {
    member(int p_y) : y(p_y) {}
    int y;
};

struct bar : foo
{
    bar() : foo(&my_member.y), my_member(42) {}
    member my_member;
};

#include <iostream>

int main()
{
    bar my_bar;
    std::cout << *my_bar.x;
}

Это хорошо определено? Законно ли брать адрес члена данных неинициализированного объекта? Я нашел этот вопрос о передаче ссылки на неинициализированный объект, но это не совсем то же самое. В этом случае я использую оператор доступа к членам. . на неинициализированный объект.

Это правда, что адрес члена данных объекта не должен изменяться при инициализации, но это не обязательно делает взятие этого адреса четко определенным. Кроме того, на странице ccpreference.com для операторов доступа к членам есть это сказать:

Первый операнд обоих операторов оценивается, даже если в этом нет необходимости (например, когда второй операнд называет статический член).

Я понимаю, что это означает, что в случае &my_member.y будет оцениваться my_member, и я считаю, что это нормально (например, int x; x; кажется хорошим), но я также не могу найти документацию, подтверждающую это.


person François Andrieux    schedule 25.04.2018    source источник
comment
Это определенно хорошо определено. А встроенный адрес оператора не имеет доступа к членскому хранилищу. Данная цитата относится только к операторам . и ->.   -  person user7860670    schedule 25.04.2018
comment
@VTT Тогда я думаю, что я спрашиваю, правильно ли определено использование . в &my_member.y.   -  person François Andrieux    schedule 25.04.2018
comment
О, так что я предполагаю, что проблема с &my_member.y на самом деле не в адресе оператора, а скорее в операторе доступа к членам, который оценивает еще не инициализированный объект. Я должен был прочитать вопрос более внимательно.   -  person user7860670    schedule 25.04.2018


Ответы (2)


Сначала уточним вопрос.

То, что вы делаете, не использует неинициализированный объект, вы используете объект не в течение его времени жизни. my_member создается после foo, поэтому время жизни my_member еще не началось в foo(&my_member.y).

Из [basic.life]

до того, как началось время жизни объекта, но после того, как хранилище, которое будет занимать объект, было выделено [...], любое значение gl, которое ссылается на исходный объект, может использоваться, но только ограниченными способами. [...] такое значение gl относится к выделенному хранилищу, и использование свойств значения gl, которые не зависят от его значения, четко определено. Программа имеет неопределенное поведение, если:

  • glvalue используется для доступа к объекту или [...]

Здесь доступ к нему означает чтение или изменение значения объекта.

Оценка my_member дает lvalue, и нет ничего, что требовало бы преобразования в prvalue, поэтому оно остается lvalue. Точно так же оценка my_member.y также является lvalue. Затем мы делаем вывод, что ни одно значение объекта не было доступно, это четко определено.

person Passer By    schedule 28.04.2018

Да, вам разрешено передавать &my_member.y конструктору foo и даже копировать указатель, что вы делаете с x(p_x).

Поведение при разыменовывании этого указателя в конструкторе foo не определено. (Но вы этого не делаете.)

person Bathsheba    schedule 25.04.2018