Подтвердить что ты не робот

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

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

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). Разве это не нарушает, равномерность равномерной инициализации?

EDIT: я пробовал с clang, и clang принимает его, так что, возможно, это ошибка GCC?

4b9b3361

Ответ 1

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

Nicol Bolas дает хорошее представление о том, что gcc на самом деле является соответствующим компилятором в соответствии со стандартом С++ 11, утвержденным FDIS, поскольку были внесены изменения в рабочий документ после этого.

Ответ 2

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

§8.5.4/3

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

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

  • В противном случае, если T является ссылочным типом, временное значение praleue типа, на которое ссылается 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 является ссылочным типом, временное значение praleue типа, на которое ссылается T, инициализируется по списку, а ссылка привязана к этому временному.

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

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

Ответ 3

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


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

Проблема - это дефект в стандарте С++ 11 и был адресован DR1288. Исправленный текст появляется в С++ 14.

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

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

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