Рассмотрим следующий пример. когда 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;
кажется в порядке) но я не могу найти документацию, подтверждающую это тоже.
Сначала давайте уточним вопрос.
То, что вы делаете, не использует неинициализированный объект, вы используете объект не в течение срока его службы. my_member
построен после foo
следовательно, время жизни my_member
не началось в foo(&my_member.y)
,
От [Basic.life]
до того, как началось время жизни объекта, но после того, как хранилище, которое будет занимать объект, было выделено […], любое значение glvalue, которое относится к исходному объекту, может использоваться, но только ограниченным образом. […] такое glvalue относится к выделенному хранилищу, и использование свойств glvalue, которые не зависят от его значения, является четко определенным. Программа имеет неопределенное поведение, если:
- glvalue используется для доступа к объекту, или […]
Здесь доступ к нему означает специально либо чтение, либо изменение значения объекта.
Оценка my_member
возвращает lvalue, и нет ничего, что требовало бы преобразования в prvalue, следовательно, оно остается lvalue. Кроме того, оценка my_member.y
это также lvalue. Затем мы заключаем, что к значению объекта не обращались, это хорошо определено.
Да, вы можете пройти &my_member.y
в foo
конструктор, и даже скопировать указатель — что вы делаете с x(p_x)
,
Поведение на разыменовании этот указатель, хотя в foo
конструктор не определен. (Но вы этого не делаете.)