В списке инициализаторов членов я могу создать ссылку на переменную-член, не входящую в список?
Рассматривать:
#include <string>
#include <iostream>
class Foo
{
public:
Foo( char const * msg ) : x( y )
{
y = msg;
}
std::string const & x;
private:
std::string y;
};
int main( int argc, char * argv[] )
{
if ( argc >= 2 )
{
Foo f( argv[1] );
std::cout << f.x << std::endl;
}
}
Это компилирует и печатает первый параметр... но я сомневаюсь, действительно ли он "легален"/хорошо сформирован. Я знаю, что список инициализаторов должен инициализировать переменные в порядке их объявления в классе, чтобы вы не ссылались на переменные, которые еще не были инициализированы. Но как насчет переменных-членов не в списке инициализаторов? Могу ли я безопасно создавать ссылки на них в качестве демонстрации?
(Например, это, конечно, бессмысленно. Это просто для разъяснения того, о чем я говорю).
Ответы
Ответ 1
Вы можете сделать это 1, потому что:
-
x
и y
находятся в области видимости ([basic.scope.class]/1). - Поскольку вы получаете ссылку после запуска конструктора ([class.cdtor]/1), и память для
y
уже получена ([basic.life]/7), эта ссылка может быть привязана к y
.
Использование этой ссылки внутри оператора конструктора (после того, как инициализация элемента завершена) также прекрасна. Это связано с тем, что y
считается инициализированным, и x
теперь относится к объекту, чье время жизни было начато.
1 - Там предостережение для юристов языка.Технически ссылка должна быть привязана к действительному объекту ([dcl.ref]/5), означающему тот, чье жизненное время началось.Однако, как и проблема с основным языком 363, он должен работать!Проблемная формулировка и возможное решение обсуждаются в выпуске 453 "Основной язык" (любезно предоставлено @TC в удаленном комментарии).Там ошибка в стандарте, но ваш код должен быть хорошо сформирован, и реализации, как правило, знают об этом.
Ответ 2
Но как насчет переменных-членов не в списке инициализаторов?
Независимо от того, находятся ли переменные в списке инициализаторов или нет, в этом отношении не имеет значения. Если переменная не находится в списке инициализаторов (и не имеет инициализатора по умолчанию), то она инициализируется по умолчанию.
y
инициализируется после x
. Это происходит не из-за списка инициализаторов членов, поскольку список инициализаторов членов не влияет на порядок инициализации членов. Члены инициализируются в порядке их объявления.
Однако, инициализируется ли y
или нет, также не имеет значения. Он хорошо сформирован для привязки ссылки к члену до инициализации элемента (кроме привязки к виртуальной базе неинициализированного элемента, который имеет UB).
Что касается безопасности (или, точнее, правильности), я рекомендую вам подумать о том, что произойдет, когда Foo
будет скопирован. К чему будет относиться x
? Это то, чего ожидал пользователь класса?
Ответ 3
Могу ли я безопасно создавать ссылки на них в качестве демонстрации?
Да, ты можешь. Адрес хранилища элемента y
известен независимо от его инициализации или нет, поэтому инициализация ссылок x(y)
является законной.