C++11: enable_if

std::enable_if is another feature taken from the Boost C++ library that now ships with every C++11 compliant compiler.

As its name says, the template struct enable_if, enables a function only if a condition (passed as a type trait) is true; otherwise, the function is undefined. In this way, you can declare several “overloads” of a method and enable or disable them depending on some requirements you need. The nice part of this is that the disabled functions will not be part of your binary code because the compiler simply will ignore them.

This feature works nicely with SFINAE.

Let’s consider the example of the post about SFINAE:

template <typename T>
struct has_iterator
{
    template <typename U>
    static char test(typename U::iterator* x);

    template <typename U>
    static long test(U* x);

    static const bool value = sizeof(test<T>(0)) == 1;
};

This struct can be used to determine in compile time if T has an iterator or not.

In my example, I want to create a string vector and an int variable and invoke a method called “show” using enable_if. The main() function would be something like this:

int main()
{
  int a = 128;
  vector<string> s = { "today", "is", "Saturday" };

  show(a);
  show(s);
}

So, how could I implement the show() functions using enable_if?

Look at their implementation:

template <typename T>
typename enable_if<!has_iterator<T>::value, void>::type show(const T& x)
{
	cout << x << endl;
}

template <typename T>
typename enable_if<has_iterator<T>::value, void>::type show(const T& x)
{
	for (auto& i : x)
		cout << i << endl;
}

It looks spooky, doesn’t it? :)

It works in this way:

enable_if is a template struct that has two type arguments: the first one is a boolean condition and the second one is the return type we want our function to return if the condition is true. The enable_if struct has a static const attribute called type that maps to the return type expressed as the second type argument passed to the struct.

So, if the condition is true, the compiler will enable the function declared using enable_if, otherwise the function will simply be ignored by the compiler.

In my example, for show(a); the first show function will be enabled and for show(s); the second one will be enabled.

enable_if could be, for example, used to enable some pieces of code depending on, say, what platform you are using to provide optimizations for that platform (rendering obsolete the #define preprocessor directive). For example, you could implement something like:

template <typename>
struct is_64_bit
{
    static const bool value = sizeof(void*) == 8;
};

template <typename T>
typename enable_if<is_64_bit<T>::value, void>::type
my_memcpy(void* target, const void* source, T n)
{
    cout << "64 bit memcpy" << endl;
}
 
template <typename T>
typename enable_if<!is_64_bit<T>::value, void>::type
my_memcpy(void* target, const void* source, T n)
{
    cout << "32 bit memcpy" << endl;
}

So, for 64-bit architectures, your 64-bit specific implementation would be enabled and the other one ignored.

To use std::enable_if you must include type_traits:

#include <type_traits>
Advertisements

5 thoughts on “C++11: enable_if

  1. Nice post. Your posts are very well written and concise. Looking for more. Thanks. Just one typo though. The vector of strings in your example is named ‘s’ but you mention it as ‘b’ elsewhere.

  2. Hi, I think the last part might be wrong. Because “enable_if::type” will not be found if “is_64_bit::value” is true — then a compilation error will occur.

  3. Hi, thanks for your post, they are very useful. Regarding what Dan says, the last part gives a compilation eror. I tested using VC++2013 on 32 bit architecture. From the documentation:

    “The type T is enabled as member type enable_if::type if Cond is true.

    Otherwise, enable_if::type is not defined.

    This is useful to hide signatures on compile time when a particular condition is not met, since in this case, the member enable_if::type will not be defined and attempting to compile using it should fail.”

    I think the normal behavior of enable_if is a failure at compile time, and the problem is that If both “enable_if<is_64_bit::value…" and "enable_if<!is_64_bit::value…" are used, always one of them will give a compilation error. So, you cannot use "enable_if<bool_expression…" and "enable_if<!bool_expression…" at the same time.

    (And there is a typo, you first name "s" a variable and then refer to it as "b".)

    1. Dan and WavePackage, you both were right.

      I modified my stuff to have it working. Actually, because of SFINAE I was able to make it work (forcing the first struct to be template instead of a normal one; not as cleaner as I would want to, but it works).

      Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s