Ответ 1
Проблема заключается в ошибке/ошибке или ошибке в стандарте С++ 03, причем разные компиляторы пытаются решить проблему по-разному. (Эта проблема больше не существует в стандарте С++ 11.)
В разделах 8.5.3/5 обоих стандартов указано, как инициализируется эта ссылка. Здесь версия С++ 03 (нумерация списка - моя):
Ссылка на тип
cv1 T1
инициализируется выражением типаcv2 T2
следующим образом:
Если выражение инициализатора
- является lvalue (но не является битовым полем), а "cv1 T1" ссылается на "cv2 T2" или
- имеет тип класса (т.е.
T2
- тип класса) и может быть неявно преобразован в lvalue типаcv3 T3
, гдеcv1 T1
является ссылочным-совместимым сcv3 T3
тогда ссылка привязана непосредственно к значению инициализатора lvalue в первом случае, а ссылка привязана к lvalue-результату преобразования во втором случае.
В противном случае эта ссылка должна быть указана в виде энергонезависимого типа (т.е.
cv1
должна бытьconst
).Если выражение инициализатора является rvalue,
T2
тип класса, аcv1 T1
является ссылочным-совместимым сcv2 T2
, ссылка привязана одним из следующих способов (выбор - реализация -определенный):
- Ссылка привязана к объекту, представленному rvalue (см. 3.10), или к под-объекту внутри этого объекта.
- Создается временный тип
cv1 T2
[sic], и вызывается конструктор для копирования всего объекта rvalue во временный. Ссылка привязана к временному или к под-объекту во временном.Конструктор, который будет использоваться для создания копии, должен быть вызван независимо от того, действительно ли выполнена копия.
В противном случае временный тип
cv1 T1
создается и инициализируется из выражения инициализатора, используя правила для инициализации без ссылки (8.5). Ссылка затем привязана к временному.
В рассматриваемом вопросе участвуют три типа:
- Тип создаваемой ссылки. Стандарты (обе версии) обозначают этот тип как
T1
. В этом случае этоstruct A
. - Тип выражения инициализатора. Стандарты обозначают этот тип как
T2
. В этом случае выражение инициализатора представляет собой переменнуюc
, поэтомуT2
-struct C
. Обратите внимание, что посколькуstruct A
не является ссылочным-совместимым сstruct C
, невозможно напрямую привязать ссылку кc
. Требуется промежуточное звено. - Тип промежуточного. Стандарты обозначают этот тип как
T3
. В этом случае этоstruct B
. Обратите внимание, что применение оператора преобразованияC::operator B()
toc
преобразует значение lvaluec
в значение r.
Инициализации на то, что я обозначил как 1.1 и 3, отсутствуют, потому что struct A
не является ссылочным-совместимым с struct C
. Необходимо использовать оператор преобразования C::operator B()
. 1.2 не работает. Поскольку этот оператор преобразования возвращает rvalue, это правило 1.2. Все, что осталось, это вариант 4, создать временный тип cv1 T1
. Строгое соблюдение версии стандарта 2003 года заставляет создавать две временные проблемы для этой проблемы, хотя достаточно только одного.
Версия стандарта 2011 года устраняет проблему путем замены опции 3 на
Если выражение инициализатора
- - это значение xvalue, класс prvalue, значение prvalue массива или функция lvalue и
cv1 T1
совместим сcv2 T2
или- имеет тип класса (т.е.
T2
- тип класса), гдеT1
не относится кT2
, а может быть неявно преобразован в значение xvalue, класс prvalue или функцию lvalue типаcv3 T3
, гдеcv1 T1
является ссылочным-совместимым сcv3 T3
,тогда ссылка привязана к значению выражения инициализатора в первом случае и к результату преобразования во втором случае (или, в любом случае, к соответствующему подобъекту базового класса). Во втором случае, если ссылка является ссылкой на rvalue, а вторая стандартная последовательность преобразований определяемой пользователем последовательности преобразования включает в себя преобразование lvalue-to-rval, программа плохо сформирована.
Похоже, что компиляторы семейства gcc выбрали строгое соответствие требованиям (избегайте создания ненужных временных рядов), в то время как другие компиляторы, которые печатают "b", выбрали намерение/исправление стандарта. Выбор строгого соблюдения не обязательно заслуживает похвалы; есть другие ошибки/ошибки в версии стандарта 2003 года (например, std::set
), где семья gcc выбрала здравый смысл в отношении строгого соответствия.