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):
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”.is_pointer_ template for pointer T* without caring about what T is really. This version inherits from std::true_type.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:
::value, e.g. is_pointer<int>::value – value is a static class member of type bool inherited from std::true_type or std::false_type;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;