Ответ 1
@dascandy правильно определил, что не так с вашим кодом. Я попытаюсь объяснить некоторые причины:
Вы ожидаете, что компилятор выведет unique_ptr<int>
, потому что аргумент int*
, а unique_ptr<int>
имеет конструктор, который принимает int*
. На мгновение пусть игнорирует тот факт, что мы используем std::unique_ptr
, и просто говорим о классе шаблона, который мы написали (и можем специализироваться).
Почему компилятор должен вывести unique_ptr<int>
? Аргумент не int
, it int*
. Почему бы не угадать unique_ptr<int*>
? Конечно, это приведет к ошибке компилятора, так как конструктор unique_ptr<int*>
не примет int*
. Если я не добавлю специализацию:
template<>
class unique_ptr<int*>
{
public:
unique_ptr(int*) {}
};
Теперь unique_ptr<int*>
будет компилироваться. Как компилятор должен знать, что выбрать, unique_ptr<int>
или unique_ptr<int*>
? Что делать, если я добавлю другую специализацию?
template<>
class unique_ptr<double>
{
public:
unique_ptr(int*) {}
};
Теперь у компилятора есть три варианта выбора, и он должен создать экземпляр шаблона с каждым возможным аргументом, чтобы найти их. Очевидно, что это невозможно, особенно с несколькими аргументами шаблона и рекурсией шаблонов.
Что вы можете сделать, это сделать функцию factory, которая соединяет выведенный тип с одним экземпляром шаблона:
template<typename T>
std::unique_ptr<T> make_unique(T* arg) { return arg; }
(конечно, это не сработает, потому что unique_ptr
не может быть скопировано, но идея действительна и используется, например, make_shared
и make_pair
.)
Некоторые примеры крайнего уродства:
Можно утверждать, что unique_ptr<shared_ptr<int>>
является допустимым соответствием для этого кода.
Или как насчет:
template<typename T>
class unique_ptr
{
public:
explicit unique_ptr(T* arg);
unique_ptr(int*, enable_if<(sizeof(T) > 16)>::type* = 0);
};