C++ Manual distinction of types when given any type T


When implementing SFINAE using std::enable_if, it is often useful to have access to helper templates that determines if a given type T matches a set of criteria.

To help us with that, the standard already provides two types analog to true and false which are std::true_type and std::false_type.

The following example show how to detect if a type T is a pointer or not, the is_pointer template mimic the behavior of the standard std::is_pointer helper:

template <typename T>
struct is_pointer_: std::false_type {};

template <typename T>
struct is_pointer_<T*>: std::true_type {};

template <typename T>
struct is_pointer: is_pointer_<typename std::remove_cv<T>::type> { }

There are three steps in the above code (sometimes you only need two):

  1. The first declaration of is_pointer_ is the default case, and inherits from std::false_type. The default case should always inherit from std::false_type since it is analogous to a "false condition".

  2. The second declaration specialize the is_pointer_ template for pointer T* without caring about what T is really. This version inherits from std::true_type.

  3. The third declaration (the real one) simply remove any unnecessary information from T (in this case we remove const and volatile qualifiers) and then fall backs to one of the two previous declarations.

Since is_pointer<T> is a class, to access its value you need to either:

  • Use ::value, e.g. is_pointer<int>::valuevalue is a static class member of type bool inherited from std::true_type or std::false_type;
  • Construct an object of this type, e.g. is_pointer<int>{} – This works because std::is_pointer inherits its default constructor from std::true_type or std::false_type (which have constexpr constructors) and both std::true_type and std::false_type have constexpr conversion operators to bool.

It is a good habit to provides "helper helper templates" that let you directly access the value:

template <typename T>
constexpr bool is_pointer_v = is_pointer<T>::value;

In C++17 and above, most helper templates already provide a _v version, e.g.:

template< class T > constexpr bool is_pointer_v = is_pointer<T>::value;
template< class T > constexpr bool is_reference_v = is_reference<T>::value;