Несмотря на синтаксические ошибки, ваш код не компилируется, потому что вы не можете частично специализировать шаблоны функций, вы можете только частично специализировать шаблоны классов. Вы можете, однако, перегружать функции. Решение этой проблемы с использованием перегрузок функций совсем не просто. Ниже я подробно описал два метода, которые вы можете использовать для решения своей проблемы, второй из которых использует частичную специализацию шаблонов.
Во-первых, если у вас есть компилятор C ++ 17, вы можете использовать if constexpr
и шаблонное программирование для получения желаемого эффекта:
template <class SmartPtr>
struct Holder {
//this is a nested helper struct, which will allow us to deduce at compile-time
// whether SmartPtr is a std::unique_ptr or not, using function overloading
template<typename T>
struct is_unique_ptr{
template<typename TT>
constexpr static std::true_type test(std::unique_ptr<TT>*);
constexpr static std::false_type test(...);
using type = decltype(test(std::declval<T*>()));
constexpr static bool value = type::value;
};
//this is a nested helper struct, which will allow us to deduce at compile-time
// whether SmartPtr is a std::shared_ptr or not, using function overloading
template<typename T>
struct is_shared_ptr{
template<typename TT>
constexpr static std::true_type test(std::shared_ptr<TT>*);
constexpr static std::false_type test(...);
using type = decltype(test(std::declval<T*>()));
constexpr static bool value = type::value;
};
typedef typename SmartPtr::element_type value_type;
//default constructor will conditionally construct m_ptr depending on its type
Holder(){
if constexpr(is_unique_ptr<SmartPtr>::value){
m_ptr = std::make_unique<value_type>();
} else if constexpr(is_shared_ptr<SmartPtr>::value){
m_ptr = std::make_shared<value_type>();
} else {
m_ptr = new value_type{};
}
}
private:
SmartPtr m_ptr;
};
Это решение не использует частичную специализацию шаблона. Однако, если вы настаиваете на использовании частичной специализации шаблона или у вас нет компилятора c ++ 17, вам придется специализировать весь класс для различных типов указателей:
//generic class Holder (identical to your initial implementation)
template <class SmartPtr>
struct Holder {
typedef typename SmartPtr::element_type value_type;
Holder()
: m_ptr(new value_type){
}
private:
SmartPtr m_ptr;
};
//this partial specialisation will be selected if template parameter is a std::unique_ptr
template<typename T>
struct Holder<std::unique_ptr<T>>{
using value_type = T;
Holder() : m_ptr(std::make_unique<value_type>()){
}
private:
std::unique_ptr<T> m_ptr;
};
//this partial specialisation will be selected if template parameter is a std::unique_ptr
template<typename T>
struct Holder<std::shared_ptr<T>>{
using value_type = T;
Holder() : m_ptr(std::make_shared<value_type>()) {
}
private:
std::shared_ptr<T> m_ptr;
};
Использование частичной специализации шаблона Примерно так, может привести к вздутию кода. Если вы решите использовать это решение, вы можете рассмотреть возможность выделения общих методов в базовый класс, от которого наследуются все специализации. Это поможет уменьшить раздувание кода.