Ответ 1
Вывод аргумента шаблона класса происходит в два этапа.
- Выведите аргументы шаблона класса.
- Затем выполните фактическую конструкцию с конкретным классом типа.
Шаг 1 поможет вам определить аргументы шаблона класса. Это не делает ничего относительно фактического конструктора (или специализации шаблона класса), который может быть использован на шаге 2.
Существование конструкторов может привести к вычету, но если данный конструктор используется для вывода, который ничего не говорит о том, используется ли он для его построения.
Итак, когда у вас только есть:
template<typename T>
struct MyAbs {
template<typename U>
MyAbs(U&& u);
};
Недопустимый вывод аргумента шаблона шаблона: у вас нет указателя на вывод в вашем конструкторе, T
- невыводимый контекст. Компилятор просто не могу понять, что T
вы хотите в MyAbs(4.7)
или MyAbs(d)
.
Когда вы добавили это:
template<typename T>
struct MyAbs {
template<typename U>
MyAbs(U&& u);
MyAbs(T const&);
};
Теперь он может! В обоих случаях он выводит T
как double
. И как только это произойдет, мы продолжим и выполняем перегрузочное разрешение, как если бы мы написали MyAbs<double>
для начала.
И здесь MyAbs<double>(4.7)
предпочитает конструктор ссылок пересылки (меньше ссылочной ссылки cv), в то время как MyAbs<double>(d)
предпочитает другой (не шаблон, предпочтительный для шаблона). И это нормально и ожидалось, просто потому, что мы использовали один конструктор для вычитания, не означает, что мы должны использовать именно этот конструктор для построения.