Question

I think about overloading std::is_pointer in C++11 to yield true for std::shared_ptr<T> as well, since the latter behaves very much as a T*.

#include <type_traits>

namespace std {

template <typename T> struct is_pointer<shared_ptr<T>> : std::true_type {};
template <typename T> struct is_pointer<shared_ptr<T const>> : std::true_type {};

}

I wonder why this overload has not already been included in the standard implementation. Is there a pitfall that I'm overlooking?

As an alternative one could of course introduce a new trait is_shared_ptr<T>.

Actually, I tried the following code in the first place:

template <typename T> 
struct is_pointer<shared_ptr<typename std::remove_cv<T>::type>>
  : std::true_type 
{};

which does not compile with GCC 4.7 due to

error: template parameters not used in partial specialization:
error:         ‘T’
Was it helpful?

Solution

std::is_pointer somehow comes from Boost and is originally meant to detect raw pointers and function pointers only, here is the bottom note you can find in Boost documentation:

is_pointer detects "real" pointer types only, and not smart pointers. Users should not specialise is_pointer for smart pointer types, as doing so may cause Boost (and other third party) code to fail to function correctly. Users wanting a trait to detect smart pointers should create their own. However, note that there is no way in general to auto-magically detect smart pointer types, so such a trait would have to be partially specialised for each supported smart pointer type.

They probably just had it behave like that in the standard library for compatibility to keep a low level of surprise for users that already used it. Anyway, as you just demonstrated it, it is easy to create your own trait to detect smart pointers.

Basically, what you are looking would be a trait that would look for types which implement the Dereferenceable concept (even though this one would also work for std::optional and not only pointers/smart pointers).

And for the sake of completeness, Boost's is_pointer design is there to detect only raw pointers and not pointer-like classes. But the other answers should already give you some pretty good information about that.

OTHER TIPS

I think about overloading std::is_pointer in C++11 to yield true for std::shared_ptr as well, since the latter behaves very much as a T*.

It does not. Good luck doing ++p or p[i] with a shared_ptr.

I wonder why this overload has not already been included in the standard implementation. Is there a pitfall that I'm overlooking?

It was not included because it would have been simply wrong: is_pointer tells you if a type is a pointer type (§3.9.2). shared_ptr is not a pointer type, so have is_pointer<shared_ptr<int>::value be true would simply be wrong.

Actually, I tried the following code in the first place:

template <typename T> 
struct is_pointer<shared_ptr<typename std::remove_cv<T>::type>>
  : std::true_type 
{};

What the hell is remove_cv doing there? The following "works" in GCC.

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

However, it has undefined behaviour. In general, you are not allowed to add specializations to templates in namespace std if they don't agree with the defined semantics. The semantics for std::is_pointer<T> are such that it only derives from std::true_type if T is a pointer type which std::shared_ptr isn't. That alone would be enough, but in this particular case the standard actually goes to the length of forbidding it explicitly (§20.9.2):

The behavior of a program that adds specializations for any of the class templates defined in this subclause is undefined unless otherwise specified.

The only template from <type_traits> that a user can add specializations to is std::common_type.

While I agree that more general type traits, like behaves_like_pointer (excuse the stupid name), is_callable (for all things having ()) or is_indexable (for array-like things) would be very useful, that is certainly not what things like is_pointer, is_function or is_array have been designed for. They are traits identifying actual hard language type categories, much the same like is_integral, is_class or is_rvalue_reference.

So overlaoding is_pointer for any kind of smart pointer would conradict that original purpose, and that purpose is a clear and unambigous one. But I still agree that additional more general conceptual type categories like is_smart_pointer, is_callable, is_nothrow_swappable or is_hashable would be very useful, too.

Other answers covered the technical details and some of the background.

The idea of type_traits in std:: is to provide primitives. You use those out of the box, and do specializations only to cover your self-defined types to follow get reports correctly for the original semantics.

What you want really (IMO) is not diverting the is_pointer template, but to have a query function that covert your semantics. So that is what you should do: your own is_maybesmart_pointer<> that reports true for original pointers and whatever else you desire. And use that in your code.

Tweaking the original by the original idea would quite possibly lead to ODR violation as how do you know the libs you link did not already use is_pointer with a shared_ptr or something?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top