Данный пример:
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 и clang 7.0.1 были использованы и не согласны с тем, что происходит в этом примере. Может ли кто-нибудь уточнить это?
В инициализации списка :
В противном случае , если в списке инициализатора есть один элемент типа E и либо T не является ссылочным типом, либо его ссылочный тип связан со ссылкой на E , объект или ссылка инициализируется из этого элемента (путем инициализации копирования для инициализации списка копирования или путем прямой инициализации для инициализации прямого списка); если для преобразования элемента в T требуется сужающее преобразование (см. ниже), программа является некорректной.
В противном случае, если T является ссылочным типом, генерируется prvalue типа, на который ссылается T. prvalue инициализирует свой результирующий объект путем копирования-инициализации списка или прямой-инициализации списка, в зависимости от вид инициализации для ссылки. Затем значение prvalue используется для прямой инициализации ссылки. [Примечание: Как обычно, привязка завершится неудачно, и программа будет неправильно сформирована, если ссылочный тип является lvalue-ссылкой на неконстантный тип. - конец примечания]
В исходной инициализации :
Для данных типов «cv1 T1» и «cv2 T2», «cv1 T1» относится к «cv2 T2», если T1 относится к тому же типу, что и T2, или T1 является базовым классом для T2 * 1035. *. «Cv1 T1» совместим с «cv2 T2», если
- T1 связан со ссылкой на T2, или
- T2 - это «функция без исключения», а T1 - это «функция», где типы функций в остальном одинаковы,
... и позже есть некоторые (лично неоднозначные) языки для пользовательских преобразований :
Например:
Если ссылка является ссылкой lvalue и выражением инициализатора
...
имеет тип класса (т. Е. T2 является типом класса), где T1 не связан со ссылкой на T2 и может быть преобразован в lvalue типа «cv3 T3», где «cv1 T1» является ссылочно-совместимым с «cv3 T3» (это преобразование выбирается путем перечисления применимых функций преобразования ([over.match.ref]) и выбора наилучшего с помощью разрешения перегрузки),
...
тогда ссылка привязывается к ... значению результата преобразования
...
В противном случае, если выражение инициализатора
...
имеет тип класса (т. е. T2 является типом класса), где T1 не связан со ссылкой на T2 и может быть преобразован в rvalue или функцию lvalue типа «cv3 T3», где «cv1 T1» совместим со ссылками с «cv3 T3»
...
тогда значение ... результата преобразования во втором случае называется преобразованным инициализатором. Если преобразованный инициализатор является prvalue, его тип T4 настраивается на тип «cv1 T4»
...
В противном случае:
- Если T1 или T2 является типом класса и T1 не связан со ссылкой на T2, пользовательские преобразования рассматриваются с использованием правил для инициализации копирования объекта типа «cv1 T1» путем пользовательского преобразования ... Результат вызова функции преобразования, как описано для инициализации копирования без ссылки, затем используется для прямой инициализации ссылки. Для этой прямой инициализации пользовательские преобразования не рассматриваются .
...
В противном случае, выражение инициализатора неявно преобразуется в значение типа «cv1 T1» . Применяется временное преобразование материализации, и ссылка привязывается к результату.
Эти правила довольно тонкие, и я не могу полностью понять каждую ситуацию.
Мне кажется, что должно генерироваться prvalue (я согласен снг), но язык инициализации ссылок и взаимодействия с инициализацией списка очень размыт.