Manual distinction of types when given any type T
suggest changeWhen 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):
- The first declaration of
is_pointer_
is the default case, and inherits fromstd::false_type
. The default case should always inherit fromstd::false_type
since it is analogous to a “false
condition”. - The second declaration specialize the
is_pointer_
template for pointerT*
without caring about whatT
is really. This version inherits fromstd::true_type
. - The third declaration (the real one) simply remove any unnecessary information from
T
(in this case we removeconst
andvolatile
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>::value
–value
is a static class member of typebool
inherited fromstd::true_type
orstd::false_type
; - Construct an object of this type, e.g.
is_pointer<int>{}
– This works becausestd::is_pointer
inherits its default constructor fromstd::true_type
orstd::false_type
(which haveconstexpr
constructors) and bothstd::true_type
andstd::false_type
haveconstexpr
conversion operators tobool
.
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;