TMP: Programming with enable_if

true_false

Enable_if is used side by side with SFINAE, so in case you don’t know what that is, please read that post first.

 

Type traits / traits classes

 

Definitions

  • Traits classes do not determine the type of the object. Instead, they provide additional information about a type, typically by defining typedefs or constants inside the trait.
  • Think of a trait as a small object whose main purpose is to carry information used by another object or algorithm to determine “policy” or “implementation details”. – Bjarne Stroustrup

 

The Basics behind a type trait

  • You use a templated structure, usually named with the type trait you are after. Eg) is_integer, is_pointer, is_void
  • The structure contains a static const bool named value which usually defaults to false
  • You make specializations of the structure representing the traits you want to expose, and have those set their bool value to true
  • You use a type trait by querying its value, like: my_type_trait<T>::value

 

Creating a type trait

Step 1: Create a base template with the default value of false

Step 2: Specialize the template for relevant types, set bool to true

This trait can be used to detect if any given type (passed as a template parameter) is void or not.

 

Please find below a more concrete example of type trait. As you can see, the idea is to simply change the value of the boolean to true for cases that want to pass the compile-time flag.

 

Compile time branching using SFINAE

Enable_if is a template that receives two parameters, a boolean and a type.

In case the compile time condition is set to true, the struct will define the same type as the 2nd parameter.

Let’s give an example how this works and how SFINAE comes into play using enable_if.

 

Enable_if for valid expression

First parameter is true, so the following definition of enable_if will be set.

type is defined as 2nd param, so the following line is translated as:

 

Enable_if for invalid expression

First parameter is false, so the following definition of enable_if will be set.

type is not defined, so the following line will result in a SFINAE drop

 

Combining type_traits with enable_if

Now, instead of hardcoding the first parameter, we can use any of the type_traits from above.

To be remembered that the 2nd parameter will be the actual value that will be used if the expression pass the compilation phase.

Assuming the following code is executed:

The following things will happen, for the first call => show(int)

  1. show(a) will pass the template argument deduction with T = int
  2. has_iterator<T>::value will be set to false, as int does not have any iterator
  3. enable_if<has_iterator<T>::value, void> will be rejected due to SFINAE
  4. enable_if<!has_iterator<T>::value, void> will be called, as int does not have an iterator
  5.  2nd parameter will be set to return value of show function

 

Uses of enable_if

 

Function return type

This is what has been presented above. It will allow a function to pass the compile phase or not.

Function argument

The same as above, but enable_if is added as a parameter instead

Class template parameter

Adding the enable_if value as a parameter of the class template

PS. We should not use this, as static_assert is much better to use.

Type traits in STL

How are type traits defined in STL?

Type traits inherit from helper base classes that are defined in the library

Implementation of is_same

 

What traits are available in the standard?

type_traits

 

Leave a comment

Your email address will not be published.