Home > Software engineering >  Define a template function according to class member variable with typetraits
Define a template function according to class member variable with typetraits

Time:06-30

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 that decltype 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 of has_member_k? I guess it relates to the type of k but I am not sure how.
  • Is the second has_member_k defined only to return a false type? I guess uint_32 is excluded somehow from the case of variadic arguments
  • Why do I need typename preceding enable_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).

  • Related