Учитывая этот пример:
int g_i = 10;
struct S {
operator int&(){ return g_i; }
};
int main() {
S s;
int& iref1 = s; // implicit conversion
int& iref2 = {s}; // clang++ error, g++ compiles fine:
// `s` is converted
// to a temporary int and binds with
// lvalue reference
int&& iref3 = {s}; // clang++ compiles, g++ error:
// cannot bind rvalue reference
// to lvalue
}
Ошибки описаны в комментариях. gcc 8.2.1 и лязг 7.0.1 использовались и расходятся во мнениях относительно того, что происходит в этом примере. Может кто-нибудь прояснить это?
Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization); if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.
Otherwise, if T is a reference type, a prvalue of the type referenced by T is generated. The prvalue initializes its result object by copy-list-initialization or direct-list-initialization, depending on the kind of initialization for the reference. The prvalue is then used to direct-initialize the reference. [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. — end note ]
Given types “cv1 T1” and “cv2 T2”, “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if
- T1 is reference-related to T2, or
- T2 is “noexcept function” and T1 is “function”, where the function types are otherwise the same,
... и позже есть некоторый (лично неоднозначный) язык пользовательских преобразований:
Например:
If the reference is an lvalue reference and the initializer expression
...
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (this conversion is selected by enumerating the applicable conversion functions ([over.match.ref]) and choosing the best one through overload resolution),
...
then the reference is bound to the ... value result of the conversion
...
Otherwise, if the initializer expression
...
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an rvalue or function lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3”
... then the value of the ... result of the conversion in the second case is called the converted initializer. If the converted initializer is a prvalue, its type T4 is adjusted to type “cv1 T4”
...
Otherwise:
- If T1 or T2 is a class type and T1 is not reference-related to T2, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion ... The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference. For this direct-initialization, user-defined conversions are not considered.
...
Otherwise, the initializer expression is implicitly converted to a prvalue of type “cv1 T1”. The temporary materialization conversion is applied and the reference is bound to the result.
Эти правила содержат множество нюансов, и я не могу полностью понять каждую ситуацию. Мне кажется, что должно быть сгенерировано prvalue (я согласен с clang), но язык инициализации ссылок и взаимодействия с инициализацией списка очень нечеткий.





Давайте прочитаем стандарт в правильном порядке, чтобы знать, какие разделы относятся к данной ситуации.
[dcl.init] / 17 говорит:
The semantics of initializers are as follows... If the initializer is a (non-parenthesized) braced-init-list or is
=braced-init-list, the object or reference is list-initialized (11.6.4) ...
Итак, мы переходим к [dcl.init.list] (11.6.4). В пункте 3 говорится:
List-initialization of an object or reference of type
Tis defined as follows: (... cases that don't apply are elided from this quotation...) Otherwise, if the initializer list has a single element of typeEand eitherTis not a reference type or its referenced type is reference-related toE... otherwise, ifTis a reference type, a prvalue of the type referenced byTis generated. The prvalue initializes its result object by copy-list-initialization or direct-list-initialization, depending on the kind of initialization for the reference. The prvalue is then used to direct-initialize the reference. [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. —end note ]
Согласно [dcl.init.ref] / 4:
Given types “cv1
T1” and “cv2T2”, “cv1T1” is reference-related to “cv2T2” ifT1is the same type asT2, orT1is a base class ofT2.
Следовательно, в вашем коде указанный тип int не связан по ссылке с типом в списке инициализаторов, а именно с S. Таким образом, с помощью [dcl.init.list] / 3 создается prvalue типа int, которое принимает форму int{s}. И, как говорится в примечании, в случае iref2 программа плохо сформирована, потому что она пытается привязать неконстантную ссылку lvalue к prvalue. В случае iref3 программа должна скомпилироваться, поскольку iref3 привязывается к результату prvalue int{s}.