The Boost C++ Libraries

Chapter 49. Boost.EnableIf

Boost.EnableIf makes it possible to disable overloaded function templates or specialized class templates. Disabling means that the compiler ignores the respective templates. This helps to prevent ambiguous scenarios in which the compiler doesn’t know which overloaded function template to use. It also makes it easier to define templates that can be used not just for a certain type but for a group of types.

Since C++11, Boost.EnableIf has been part of the standard library. You can call the functions introduced in this chapter without using a Boost library; just include the header file type_traits.

Example 49.1. Overloading functions with boost::enable_if on their return value
#include <boost/utility/enable_if.hpp>
#include <type_traits>
#include <string>
#include <iostream>

template <typename T>
typename boost::enable_if<std::is_same<T, int>, T>::type create()
{
  return 1;
}

template <typename T>
typename boost::enable_if<std::is_same<T, std::string>, T>::type create()
{
  return "Boost";
}

int main()
{
  std::cout << create<std::string>() << '\n';
}

Example 49.1 defines the function template create(), which returns an object of the type passed as a template parameter. The object is initialized in create(), which accepts no parameters. The signatures of the two create() functions don’t differ. In that respect create() isn’t an overloaded function. The compiler would report an error if Boost.EnableIf didn’t enable one function and disable the other.

Boost.EnableIf provides the class boost::enable_if, which is a template that expects two parameters. The first parameter is a condition. The second parameter is the type of the boost::enable_if expression if the condition is true. The trick is that this type doesn’t exist if the condition is false, in which case the boost::enable_if expression is invalid C++ code. However, when it comes to templates, the compiler doesn’t complain about invalid code. Instead it ignores the template and searches for another one that might fit. This concept is known as SFINAE which stands for Substitution Failure Is Not An Error.

In Example 49.1 both conditions in the boost::enable_if expressions use the class std::is_same. This class is defined in the C++11 standard library and allows you to compare two types. Because such a comparison is either true or false, it’s sufficient to use std::is_same to define a condition.

If a condition is true, the respective create() function should return an object of the type that was passed to create() as a template parameter. That’s why T is passed as a second parameter to boost::enable_if. The entire boost::enable_if expression is replaced by T if the condition is true. In Example 49.1 the compiler sees either a function that returns an int or a function that returns a std::string. If create() is called with any other type than int or std::string, the compiler will report an error.

Example 49.1 displays Boost.

Example 49.2. Specializing functions for a group of types with boost::enable_if
#include <boost/utility/enable_if.hpp>
#include <type_traits>
#include <iostream>

template <typename T>
void print(typename boost::enable_if<std::is_integral<T>, T>::type i)
{
  std::cout << "Integral: " << i << '\n';
}

template <typename T>
void print(typename boost::enable_if<std::is_floating_point<T>, T>::type f)
{
  std::cout << "Floating point: " << f << '\n';
}

int main()
{
  print<short>(1);
  print<long>(2);
  print<double>(3.14);
}

Example 49.2 uses boost::enable_if to specialize a function for a group of types. The function is called print() and expects one parameter. It can be overloaded, although overloading requires you to use a concrete type. To do the same for a group of types like short, int or long, you can define an appropriate condition using boost::enable_if. Example 49.2 uses std::is_integral to do so. The second print() function is overloaded with std::is_floating_point for all floating point numbers.

Exercise

Make print_has_post_increment() write to standard output whether a type supports the post-increment operator. For example, for int the program should output int has a post increment operator:

#include <string>

template <class T>
void print_has_post_increment()
{
    // TODO: Implement this function.
}

int main()
{
    print_has_post_increment<int>();
    print_has_post_increment<long>();
    print_has_post_increment<std::string>();
}