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

Вопрос поднялся в контексте этого ответа.

Рассмотрим пример:

struct foo {
    int value;
    operator int&(){ return value; }
    operator int(){ return value; }
};

int main () {
    int &a(foo{}); // #1
    //int &b{foo{}}; // #2 -- ambiguity
    int &c = foo{}; // #3
    //int &d = {foo{}}; // #4-- ambiguity
    int &d { a }; // #5
    int &e = { a }; // #6
    (void)a;
    (void)c;
    (void)d;
    (void)e;
}

Я не понимаю, почему # 2 и # 4 вызывают неоднозначность, а # 1 и # 3 - нет. Поэтому возникает вопрос: почему инициализация прямого списка вызывает неоднозначность для неявного приведения к ссылке, если объявлены операторы литья к типу и ссылка на тип?

Ответ 1

Инициализация списка, когда используется для инициализации ссылки, примет перечисленные значения и преобразует их в prvalue (aka: временный), который будет использоваться для прямой инициализации ссылки.

Итак, int &b{foo{}} функционально эквивалентно int &b(int(foo{})). Это неоднозначно; он может генерировать int через operator int или operator int&.

Но даже если бы это не было двусмысленным, вы все равно получали бы ссылку на константу без знака. Это незаконно. Поэтому этот код никогда не срабатывал.

Брексированные-init-списки (фигурные скобки) инициализируют объекты, а не ссылки на объекты. Если у вас уже есть объект и вы хотите получить ссылку на него, не используйте скопированные init-списки.


Но в этом случае почему компилятор принимает # 5?

Поскольку инициализация списка представляет собой ряд правил с приоритетом. Правило, которое имеет более высокий приоритет, чем тот, который я указал выше, относится к списку с привязкой-инициализацией, который содержит одно значение, тип которого идентичен типу инициализации. # 5 и 6 просто так соответствуют этому счету, так как d, e и a - все int& s.

Но если вы просто возьмете мой совет и не будете использовать braced-init-lists, когда вы не пытаетесь создать объект, вам не придется беспокоиться о подобных случаях.