@ dascandy правильно определил, что не так с вашим кодом.Я попытаюсь дать некоторые объяснения:
Вы ожидаете, что компилятор выведет unique_ptr<int>
, потому что аргумент - int*
, а unique_ptr<int>
имеет конструктор, который принимает int*
.На мгновение давайте проигнорируем тот факт, что мы используем std::unique_ptr
, и просто поговорим о классе шаблона, который мы написали (и можем специализировать).
Почему компилятор должен выводить unique_ptr<int>
?Аргумент не int
, это 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*) {}
};
Теперь у компилятора есть три варианта выбора, и он должен создать экземпляр шаблона со всеми возможными аргументами, чтобы найти их.Ясно, что это невозможно, особенно с несколькими аргументами шаблона и рекурсией шаблона.
Что вы можете сделать, это создать фабричную функцию, которая связывает выведенный тип с одним экземпляром шаблона:
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);
};