10

I want to get a templatized way of finding if a type is a shared_ptr and based on that I want to have a new specialization of a function.

Example main function is,

template <class T> inline
void CEREAL_LOAD_FUNCTION_NAME( RelaxedJSONInputArchive & ar,    NameValuePair<T> & t )
{
    std::cout << " CEREAL_LOAD_FUNCTION_NAME NameValuePair 1 " << std::endl;
     ar.setNextName( t.name );
     ar( t.value );
}

If t.value is shared_ptr then I want to have a different function specialization. I have tried below,

template <class T> inline
typename std::enable_if<is_pointer<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME( RelaxedJSONInputArchive & ar, NameValuePair<T> & t )
 {
    std::cout << " CEREAL_LOAD_FUNCTION_NAME NameValuePair 2 " << std::endl;
   ar.setNextName( t.name );
   ar( t.value );
  }

But it does not seem to work. These are part of c++11 cereal library. Which I am trying to customize.

Mr.100
  • 141
  • 3
  • 11

3 Answers3

17

the following may help:

template<typename T> struct is_shared_ptr : std::false_type {};
template<typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};

then you can do the following to get the correct function:

template <class T> 
typename std::enable_if<is_shared_ptr<decltype(std::declval<T>().value)>::value, void>::type
func( T t )
{
    std::cout << "shared ptr" << std::endl;
}

template <class T> 
typename std::enable_if<!is_shared_ptr<decltype(std::declval<T>().value)>::value, void>::type
func( T t )
{
    std::cout << "non shared" << std::endl;
}

live demo

Biggy Smith
  • 910
  • 7
  • 14
  • Will it work in my case. Actually I have to get a new specialization of the function based on the type of a member called value. If T::value is shared_ptr then I need new specialization. – Mr.100 Jan 25 '17 at 13:57
  • 1
    This looks like the correct answer for the question. – Alexis Wilke Apr 26 '21 at 17:05
11

This is a basic case of template specialization. The following is a type trait that determines if a type T is a shared_ptr or not. It can be used the same way std::is_pointer, which you already use.

#include <memory>
#include <type_traits>

template<class T>
struct is_shared_ptr : std::false_type {};

template<class T>
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};

Demonstration :

static_assert(is_shared_ptr<std::shared_ptr<int>>::value == true, "");
static_assert(is_shared_ptr<int>::value == false, "");
François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • I like the simplicity of this answer. – Alexis Wilke Apr 26 '21 at 17:04
  • This definitely works and I also love the simplicity, but I don't understand why it works. Can anyone explain or point me to cpp reference or similar explaining this? – Iron Attorney Apr 28 '23 at 12:57
  • 1
    @IronAttorney The answer contains a link to https://en.cppreference.com/w/cpp/language/partial_specialization which explains what partial specialization is. It basically says "there is a class template call `is_shared_ptr` with 1 type argument" and then says "when the template argument is a type of `std::shared_ptr` use this version of the template instead". – François Andrieux Apr 28 '23 at 13:14
  • Yeah sorry, I didn't give much detail to what was confusing me. So that is the right area, but to me it reads like you'll end up with a specialisation of `std::shared_ptr>` as `T` is already a `std::shared_ptr`. I'd have thought you'd need something more like `template – Iron Attorney Apr 28 '23 at 15:32
  • That confusion aside, you have helped me properly get what is happening here. The partial specialisation has a base class of `std::true_type`. That's cleared the fog alot there, thanks! – Iron Attorney Apr 28 '23 at 15:36
  • Nevermind sorry, the specialisation part all just clicked... – Iron Attorney Apr 28 '23 at 16:30
3

If the type provided is itself a std::shared_ptr of some unknown type T, then the following use of SFINAE should help! Since all smart pointers provide a member type "element_type" we can specialize for std::shared_ptr<T>, std::weak_ptr<T>, and std::unique_ptr<T> as follows:

template<typename T, typename Enable = void>
struct is_smart_pointer
{
    enum { value = false };
};

template<typename T>
struct is_smart_pointer<T, typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, std::shared_ptr<typename T::element_type>>::value>::type>
{
    enum { value = true };
};

template<typename T>
struct is_smart_pointer<T, typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, std::unique_ptr<typename T::element_type>>::value>::type>
{
    enum { value = true };
};

template<typename T>
struct is_smart_pointer<T, typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, std::weak_ptr<typename T::element_type>>::value>::type>
{
    enum { value = true };
};