В списке инициализаторов членов я могу создать ссылку на переменную-член, не входящую в список?

Рассматривать:

#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, потому что:

  1. x и y находятся в области видимости ([basic.scope.class]/1).
  2. Поскольку вы получаете ссылку после запуска конструктора ([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) является законной.