Дан класс 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})
не выполняет инициализацию списка b
- инициализация списка (копирования) применяется к самому параметру конструктора.
Поскольку аргумент является списком инициализатора, последовательность неявного преобразования, необходимая для сопоставления аргумента с параметром, определяется в терминах последовательности инициализации списка [over.ics.list] / p1:
Когда аргумент является списком инициализаторов ([dcl.init.list]), это не выражение, и для его преобразования в тип параметра применяются специальные правила.
Он гласит:
[...], если параметр является неагрегированным классом X и разрешение перегрузки согласно 13.3.1.7 выбирает единственный лучший конструктор X для выполнения инициализации объекта типа X из списка инициализаторов аргументов, последовательность неявного преобразования представляет собой определяемую пользователем последовательность преобразования, а вторая стандартная последовательность преобразования - преобразование идентичности. Если жизнеспособны несколько конструкторов, но ни один из них не лучше других, последовательность неявного преобразования является последовательностью неоднозначного преобразования. Пользовательские преобразования разрешены для преобразования элементов списка инициализаторов в типы параметров конструктора, за исключением случаев, указанных в 13.3.3.1.
Чтобы №1 был жизнеспособным, должен быть действительным следующий вызов:
A a = {0};
что верно из-за [over.match.list] / p1:
- Если жизнеспособный конструктор списка инициализаторов не найден, снова выполняется разрешение перегрузки, где все функции-кандидаты являются конструкторами класса T
, а список аргументов состоит из элементов списка инициализаторов.
т.е. класс A
имеет конструктор, который принимает аргумент int
.
Чтобы номер 2 был действительным кандидатом, должен быть действительным следующий вызов:
const B& b = {0};
который согласно [over.ics.ref] / p2:
Когда параметр ссылочного типа не привязан непосредственно к выражению аргумента, последовательность преобразования - это та последовательность, которая требуется для преобразования выражения аргумента в ссылочный тип в соответствии с [over.best.ics]. Концептуально эта последовательность преобразования соответствует копированию-инициализации временного объекта указанного типа с помощью выражения аргумента. Любое различие в CV-квалификации верхнего уровня учитывается самой инициализацией и не является преобразованием.
переводится на:
B b = {0};
Еще раз, следуя [over.ics.list] / p6 :
Пользовательские преобразования разрешены для преобразования элементов списка инициализатора в типы параметров конструктора [...]
компилятору разрешено использовать определяемое пользователем преобразование:
A(int);
для преобразования аргумента 0
в параметр конструктора B
A
.
Для кандидата №3 применяются те же рассуждения, что и в случае №2. В конце концов, компилятор не может выбрать между вышеупомянутыми последовательностями неявного преобразования {citation required} и сообщает о неоднозначности.
person
Piotr Skotnicki
schedule
01.05.2017