Ответ 1
Учитывая класс, A
с определяемым пользователем конструктором:
struct A
{
A(int) {}
};
и еще один, B
, принимающий A
как параметр конструктора:
struct B
{
B(A) {}
};
то для выполнения инициализации, как показано ниже:
B b({0});
компилятор должен учитывать следующие кандидаты:
B(A); // #1
B(const B&); // #2
B(B&&); // #3
пытается найти неявную последовательность преобразований от {0}
к каждому из параметров.
Обратите внимание, что B b({0})
не list-initialize B
- инициализация списка (copy-) применяется к самому параметру конструктора.
Поскольку аргумент представляет собой список инициализаторов, неявная последовательность преобразования, необходимая для сопоставления аргумента параметру, определяется в терминах последовательности инициализации списка [over.ics.list]/р1:
Когда аргумент представляет собой список инициализаторов ([dcl.init.list]), это не выражение и специальные правила для преобразования его в тип параметра.
Он гласит:
[...], если параметр является неагрегатным классом X и разрешением перегрузки на 13.3.1.7 выбирает один лучший конструктор X для выполнения инициализации объекта типа X из списка инициализатора аргументов, неявная последовательность преобразования представляет собой пользовательскую последовательность преобразования со вторым стандартным преобразованием последовательность преобразования идентичности. Если несколько конструкторов жизнеспособны, но ни один не лучше других, неявная последовательность преобразований - это неоднозначная последовательность преобразований. Разрешены определенные пользователем преобразования. для преобразования элементов списка инициализаторов в типы параметров конструктора, за исключением случаев, указанных в 13.3.3.1.
Для того, чтобы # 1 был жизнеспособным, должен быть действителен следующий вызов:
A a = {0};
что является правильным из-за [over.match.list]/p1:
- Если не найден жизнеспособный конструктор списка инициализаторов, разрешение перегрузки выполняется снова, где функции-кандидаты являются всеми конструкторами класса
T
, а список аргументов состоит из элементов списка инициализаторов.
i.e, class A
имеет конструктор, который принимает аргумент int
.
Чтобы №2 был действительным кандидатом, должен быть действителен следующий вызов:
const B& b = {0};
который согласно [over.ics.ref]/p2:
Когда параметр ссылочного типа не привязан непосредственно к выражению аргумента, последовательность преобразования является той, которая требуется для преобразования выражения аргумента в ссылочный тип в соответствии с [over.best.ics]. Понятно, что эта последовательность преобразования соответствует инициализации копирования временного ссылочного типа с выражением аргумента. Любая разница в квалификационной квалификации верхнего уровня включается самой инициализацией и не представляет собой преобразования.
переводит на:
B b = {0};
Еще раз, после [over.ics.list]/p6:
Пользовательские преобразования разрешены для преобразования элементов списка инициализаторов в типы параметров конструктора [...]
компилятору разрешено использовать пользовательское преобразование:
A(int);
чтобы преобразовать аргумент 0
в B
параметр конструктора A
.
Для кандидата № 3 те же рассуждения применимы, что и в # 2. В конце концов, компилятор не может выбирать между вышеупомянутыми неявными последовательностями преобразования {citation} и сообщает о двусмысленности.