I'm having some difficulties understanding a piece of code that is using typetraits.
Suppose I want to define a template function that works on some classes that has a member variable k
of type uint32_t
. (Other classes will have another template function).
The piece of code I have found is the following:
template <typename T>
auto has_member_k(std::uint32_t) -> decltype(std::declval<T>().k, std::true_type{});
template <typename T>
auto has_member_k(...) -> std::false_type;
template <typename MyType>
typename std::enable_if<decltype(has_member_k<MyType>(0UL))::value, MyType>::type MyFunction(/*some args*/)
{
/*some code*/
}
Here is what I understood:
- Regarding
auto has_member_k(std::uint32_t) -> decltype(std::declval<T>().k, std::true_type{});
decltype
is used to verify that the first expression is valid, the second is used to specify thatdecltype
should return in case the first expression is valid. auto has_member_k(...) -> std::false_type;
returns always the equivalent of false type- if
enable_if
's condition (first part) is true, its result is the second argument, otherwise function declaration is ignored
The final result is that the function is declared only if the class has a member k
.
My questions are the following:
- (Why) do we need the
std::uint32_t
as input argument ofhas_member_k
? I guess it relates to the type ofk
but I am not sure how. - Is the second
has_member_k
defined only to return a false type? I guessuint_32
is excluded somehow from the case of variadic arguments - Why do I need
typename
precedingenable_if
?
CodePudding user response:
(Why) do we need the std::uint32_t as input argument of has_member_k? I guess it relates to the type of k but I am not sure how.
Its a trick to be able to call either has_member_k(std::uint32_t)
or has_member_k(...)
(in case enable_if
discards the first). When SFINAE does not kick in then both templates could be used for the call has_member_k<MyType>(0UL)
, but the first is chosen by overload resolution and there is no ambiguity.
Is the second has_member_k defined only to return a false type?
Yes. has_member_k<MyType>(0UL))::value
is used for the enable_if
and it needs to be either true
or false
.
Why do I need typename preceding enable_if?
Because enable_if< .... >::type
is a dependant type (it depends on the template parameter). In a nutshell, the typename
is needed to ensure the compiler that enable_if< whatever >::type
is really meant to be a type (consider that in principle there could be specializations where type
is a member but not a type). In newer code you'll find the alias template std::enable_if_t
which does not need the typename
.
Concerning the points you did understand...
[...] decltype is used to verify that the first expression is valid, the second is used to specify that decltype should return in case the first expression is valid.
Its an application of the comma operator. For more details I refer you to How does the Comma Operator work
auto has_member_k(...) -> std::false_type; returns always the equivalent of false type
Note that there is no definition of has_member_k
. decltype
is only used to deduce the return type and for that no definition is needed. The function is never actually called. Its not an "equivalent" but it is std::false_type
.
if enable_if's condition (first part) is true, its result is the second argument, otherwise function declaration is ignored
Exactly. If the condition is false
then enable_if
has no member alias called type
. This is a substitution failure but not an error and the function is discarded. This is SFINAE (substitution failure is not an error).