Ответ 1
(Все цитаты из N4140, С++ 14 FD)
TL; DR: код хорошо сформирован, это (или была) ошибка GCC.
Правила инициализации ссылок описаны в [dcl.init.ref]/5. Сначала я покажу вам пулю, которая не покрывает ее, - если вы хотите пропустить это, переходите к третьей цитате.
В противном случае ссылка должна быть ссылкой lvalue на нелетучий тип const (т.е. cv1 должен быть
const
), или ссылка должна быть ссылкой rvalue.
- Если выражение инициализатора
- - это значение xvalue (но не бит-поле), класс prvalue, значение prvalue массива или функция lvalue и "cv1
T1
" ссылаются на "cv2T2
" , или- имеет тип класса (т.е.
T2
- тип класса), гдеT1
не ссылается наT2
, а может быть преобразован в значение x, class prvalue или функция lvalue типа "cv3T3
", где "cv1T1
" ссылается на "cv3T3
" (см. 13.3.1.6), тогда ссылка привязана к значению выражения инициализатора в первый случай и к результату преобразования во втором случае (или, в любом случае, к соответствующему подобъекту базового класса).
И ссылочная компиляция определена в [dcl.init.ref]/4 1.
Теперь рассмотрим связанный 13.3.1.6:
В условиях, указанных в 8.5.3, ссылка может быть связана непосредственно к значению glvalue или классу, которое является результатом применения функция преобразования в выражение инициализатора. перегрузка разрешение используется для выбора функции преобразования для вызова. Предполагая, что "cv1
T
" является базовым типом ссылки инициализируется, а "cvS
" - это тип инициализатора выражение,S
тип класса, функции-кандидата выбирается следующим образом:
- Рассматриваются функции преобразования
S
и его базовые классы. Те неявные функции преобразования, которые не являются скрытый вS
и тип вывода "lvalue reference to cv2T2
" ( при инициализации ссылки lvalue или ссылки rvalue на функция) или "cv2T2
" [..], где "cv1T
" является ссылочным (8.5.3) с "cv2T2
" , являются кандидатскими функциями. Для прямой инициализации [..].
Как вы можете видеть, ваша функция преобразования не является кандидатом после этого абзаца. Таким образом, следующая пуля в [dcl.init]/5 применима:
В противном случае:
- Если
T1
- тип класса, определяемые пользователем преобразования рассматриваются с использованием правил для копирования-инициализации объекта типа "cv1T1
" путем пользовательского преобразования (8.5, 13.3.1.4); плохо сформированный, если соответствующая неосновная копия-инициализация было бы плохо сформировано. Результат обращения к конверсии функция, как описано для инициализации без ссылки, является затем используется для прямого инициализации ссылки. Программа плохо сформированный, если прямая инициализация не приводит к прямой или если оно связано с пользовательским преобразованием.
Обратите внимание, что фраза "программа плохо сформированный, если соответствующая неосновная копия-инициализация было бы плохо сформировано "может означать, что как
B b;
A a = b;
плохо сформирован, программа плохо сформирована. Я считаю, что это недостаток или неопределенность в формулировке, но не причина, по которой GCC не принимает код. Разумеется, формулировка направлена исключительно на инициализацию, а не на то, что в первую очередь может быть создан самый производный объект типа T1
(aka A
).
Наконец, 13.3.1.4 принимает нашу функцию преобразования:
Предполагая, что "cv1
T
" является типом инициализированного объекта, сT
тип класса, выбранные функции выбираются как следующим образом:
- Конструкторы преобразования (12.3.1) из
T
являются функциями-кандидатами.- Когда тип выражения инициализатора представляет собой тип класса "cv
S
", неявные функции преобразованияS
и его базы классы. [..]. Те, которые не скрыты внутриS
и выдать тип, чья неквалифицированная версия имеет тот же тип, что иT
или является производным классом, являются кандидатскими функциями.
Теперь последний вопрос: в
A const& ref(Ad());
ref
связан непосредственно. И это так.
Таким образом, ссылка на исходный параметр привязывается напрямую, и не должен быть создан самый производный объект типа A
.
Предположительно, что GCC считает, что временный тип A
должен быть инициализирован, и ссылка должна быть привязана к этому временному. Или это педантично следует вышеперечисленной формулировке, что очень маловероятно.
1)
Указанные типы "cv1
T1
" и "cv2T2
" , "cv1T1
" ссылаются на "cv2T2
" , еслиT1
- это тот же тип, что иT2
, илиT1
- базовый классT2
. "cv1T1
" ссылается на "cv2T2
" , еслиT1
ссылка, связанная сT2
, а cv1 - это та же самая cv-квалификация, что и или более высокая cv-квалификация, чем cv2.