Почему я не могу инициализировать ссылку в списке инициализаторов с равномерной инициализацией?

Вот почему это:

struct S {};

struct T
{
    T(S& s) : s{s} {}

    S& s;
};

int main()
{
    S s;
    T t{s};
}

дай мне ошибку компилятора с GCC 4.7:

test.cpp: In constructor 'T::T(S&)':
test.cpp:5:18: error: invalid initialization of non-const reference of type 'S&' from an rvalue of type '<brace-enclosed initializer list>'

?

Чтобы исправить ошибку, я должен изменить s{s} в s(s), Разве это не нарушает равномерность равномерной инициализации?

РЕДАКТИРОВАТЬ: Я пытался с Clang, и Clang принимает это, так что, возможно, это ошибка GCC?

3 ответа

Решение

Да, это ошибка. Это что-то новое, и за него проголосовал рабочий документ в феврале 2012 года ( ссылка).

Никол Болас отмечает, что gcc на самом деле является соответствующим компилятором в соответствии со стандартом C++11, утвержденным FDIS, потому что после этого были внесены изменения в рабочий документ.

Я считаю, что это ошибка в компиляторе. Два параграфа, которые имеют дело с инициализацией ссылки посредством инициализации списка, (в n3337):

§8.5.4 / 3

Инициализация списка объекта или ссылки типа T определяется следующим образом:

  • В противном случае, если список инициализатора имеет единственный элемент типа E и либо T не является ссылочным типом, либо его ссылочный тип связан со ссылкой на E, объект или ссылка инициализируются из этого элемента; если для преобразования элемента в T требуется сужающее преобразование (см. ниже), программа является некорректной.

  • В противном случае, если T является ссылочным типом, предварительное значение временного типа, на которое ссылается T, инициализируется списком, и ссылка привязывается к этому временному объекту. [Примечание: Как обычно, привязка завершится неудачно, и программа будет некорректной, если ссылочный тип является lvalue-ссылкой на неконстантный тип. - конец примечания]

Компилятор, кажется, применяет последний абзац, когда он должен применять первый, так как связанный со ссылкой определяется как

8.5.3/4

Для заданных типов "cv1 T1" и "cv2 T2" "cv1 T1" связан со ссылкой на "cv2 T2", если T1 имеет тот же тип, что и T2, или T1 является базовым классом T2.

В случае вопроса типы ссылки и инициализатора внутри списка фигурных скобок одинаковы, что означает, что инициализация должна быть действительной.


В проекте FDIS в эквивалентных пунктах порядок был обратным. Это означает, что черновик FDIS (n3290) не позволял инициализировать скобками список *lvalue*s. С другой стороны, при чтении текста кажется очевидным, что это ошибка в стандарте и что намерение имело порядок n3337:

  • В противном случае, если T является ссылочным типом, предварительное значение временного типа, на которое ссылается T, инициализируется списком, и ссылка привязывается к этому временному объекту.

  • В противном случае, если список инициализаторов имеет один элемент, объект или ссылка инициализируются из этого элемента; если для преобразования элемента в T требуется сужающее преобразование (см. ниже), программа является некорректной.

Порядок в этом документе означает, что, поскольку все ссылочные типы обрабатываются первым предложением, упоминание ссылки в следующем абзаце не имеет смысла.

(Примечание: я пишу этот ответ с пользой 2 года назад с момента первоначального вопроса; и помещаю часть информации из комментариев в реальный ответ, чтобы ее можно было найти).


Конечно, инициализация ссылки типа S& со ссылкой также типа S& должен связывать напрямую.

Проблема является дефектом в стандарте C++11, который был устранен DR1288. Исправленный текст появляется в C++14.

Комитет пояснил, что исправленный текст - это то, что предназначалось для C++11, и поэтому "соответствующий компилятор" должен реализовать исправленную версию.

g++ 4.8 следовал опубликованному тексту стандарта C++11; однако, как только эта проблема появилась, g++ 4.9 реализовал исправленную версию, даже с -std=c++11 переключатель.

Обратите внимание, что проблема не ограничивается списками инициализатора конструктора, например: S s; S &t{s}; не работает в g ​​++ 4.8 и не работает S s; S &t = s; S &u { t };

Другие вопросы по тегам