Search code examples
c++c++11templatessfinaetypechecking

How to check if a type is a smart pointer or a reference to a smart pointer


I am trying to implement a structure that checks at compile time if a given type is a smart pointer or a reference to a smart pointer.

I rewrote this solution (that does not work for references):

template<typename T, typename Enable = void>
struct IsSmartPointer : std::false_type {
};

template<typename T>
struct IsSmartPointer<T,
        typename std::enable_if<
                std::is_same<
                        typename std::decay_t<T>, std::unique_ptr<typename std::decay_t<T>::element_type>
                >::value
        >
> : std::true_type {
};

template<typename T>
struct IsSmartPointer<T,
        typename std::enable_if<
                std::is_same<
                        typename std::decay_t<T>, std::shared_ptr<typename std::decay_t<T>::element_type>
                >::value
        >
> : std::true_type {
};

template<typename T>
struct IsSmartPointer<T,
        typename std::enable_if<
                std::is_same<
                        typename std::decay_t<T>, std::weak_ptr<typename std::decay_t<T>::element_type>
                >::value
        >
> : std::true_type {
};

I feel this implementation is very close to the right solution. However, the code below prints zeros:

std::cout << IsSmartPointer<int>::value                          << '\n'
          << IsSmartPointer<const std::shared_ptr<int> &>::value << '\n'
          << IsSmartPointer<std::shared_ptr<int> &>::value       << '\n'
          << IsSmartPointer<const std::shared_ptr<int>>::value   << '\n'
          << IsSmartPointer<std::shared_ptr<int>>::value
          << std::endl;

Could you try to find a mistake because I have run out of ideas?


Solution

  • You wrote std::enable_if<...>, when it should be std::enable_if_t<...> or std::enable_if<...>::type.

    std::enable_if<...> is a distinct type that is never going to be void. So your partial specializations will never be used if the second template argument is defaulted to void.


    Also note that you are forgetting custom deleters. std::unique_ptr has a second template argument for a custom deleter type that you will not catch if it isn't specified to the default.