Partners: KDAB and Whole Tomato Software

18 February 2016

Notes on C++ SFINAE

Starting with C++ SFINAE

This time I’d like to tackle a bit more complex problem: SFINAE. I’m not using this paradigm on a daily basis, but I’ve stumbled across it several times and I thought it might be worth trying to understand this topic.

  • What is SFINAE?
  • Where can you use it?
  • Do you need this on a daily basis?

Let’s try to answer those questions.

In the article:

Update: Look here for the follow up blog posts.

Note: I'd like to thank kj for reviewing this article and providing me a valuable feedback from the early stage of the writing process. Also many thanks goes to GW who reviewed the beta version.

Intro

First thing: if you have more time, please read An introduction to C++’s SFINAE concept: compile-time introspection of a class member by Jean Guegant. This is an awesome article that discusses SFINAE more deeply that I’ve ever found in other places. Highly recommended resource.

Still reading? Great! :) Let’s start with some basic ideas behind this concept:

Very briefly: the compiler can actually reject code that “would not compile” for a given type.

From Wiki:

Substitution failure is not an error (SFINAE) refers to a situation in C++ where an invalid substitution of template parameters is not in itself an error. David Vandevoorde first introduced the acronym SFINAE to describe related programming techniques.

We’re talking here about something related to templates, template substitution and compile time only… possibly quite a scary area!

A quick example: see it also on coliru online cpp compiler

struct Bar
{
    typedef double internalType;  
};

template <typename T> 
typename T::internalType foo(const T& t) { 
    cout << "foo<T>" << endl; 
    return 0; 
}

int main()
{
    foo(Bar());
    foo(0); // << error!
}

We have one awesome template function that returns T::internalType and we call it with Bar and int param types.

The code, of course, will not compile. The first call of foo(Bar());is a proper construction, but the second call generates the following error (GCC):

 no matching function for call to 'foo(int)'
 ...
 template argument deduction/substitution failed:

When we make a simple correction and provide a suitable function for int types. As simple as:

int foo(int i) { cout << "foo(int)" << endl; return 0; }

The code can be built and run.

Why is that?

Obviously, when we added an overloaded function for the int type, the compiler could find a proper match and invoke the code. But in the compilation process the compiler also ‘looks’ at the templated function header. This function is invalid for the int type, so why was there not even a warning reported (like we got when there was no second function provided)? In order to understand this, we need to look at the process of building the overload resolution set for a function call.

Overload Resolution

When the compiler tries to compile a function call (simplified):

  • Perform a name lookup
  • For function templates the template argument values are deduced from the types of the actual arguments passed in to the function.
    • All occurrences of the template parameter (in the return type and parameters types) are substituted with those deduced types.
    • When this process leads to invalid type (like int::internalType) the particular function is removed from the overload resolution set. (SFINAE)
  • At the end we have a list of viable functions that can be used for the specific call. If this set is empty, then the compilation fails. If more than one function is chosen, we have an ambiguity. In general, the candidate function, whose parameters match the arguments most closely is the one that is called.

Compiling a function call

In our example: typename T::internalType foo(const T& t) was not a good match for int and it was rejected from overload resolution set. But at the end, int foo(int i) was the only option in the set, so the compiler did not reported any problems.

Where can I use it?

I hope you get a basic idea what SFINAE does, but where can we use this technique? A general answer: whenever we want to select a proper function/specialization for a specific type.

Some of the examples:

  • Call a function when T has a given method (like call toString() if T has toString method)
  • Nice example here at SO of detecting count of object passed in initializer list to a constructor.
  • Specialize a function for all kind of type traits that we have (is_integral, is_array, is_class, is_pointer, etc… more traits here)
  • Foonathan blog: there is an example of how to count bits in a given input number type. SFINAE is part of the solution (along with tag dispatching )
  • Another example from foonathan blog - how to use SFINAE and Tag dispatching to construct range of objects in a raw memory space.

enable_if

One of the main uses of SFINAE can be found in enable_if expressions.

enable_if is a set of tools that internally use SFINAE. They allow to include or exclude overloads from possible function templates or class template specialization.

For example:

template <class T>
typename enable_if<is_arithmetic<T>::value, T>::type 
foo(T t)
{
  cout << "foo<arithmetic T>" << endl;
  return t;
}

This function ‘works’ for all the T types, that are arithmetic (int, long, float…). If you pass other type (for instance MyClass), it will fail to instantiate. In other words, template instantiation for non-arithmetic types are rejected from overload resolution sets. This construction might be used as template parameter, function parameter or as function return type.

enable_if<condition, T>::type will generate T, if the condition is true, or an invalid substitution if condition is false.

enable_if can be used along with type traits to provide the best function version based on the trait criteria.

As I see it, most of the time it’s better to use enable_if than your custom SFINAE versions of the code. enable_if is probably not that super nice looking expression, but this is all we have before concepts in C++17… or C++20.

Expression SFINAE

C++11 has even more complicated option for SFINAE.

n2634: Solving the SFINAE problem for expressions

Basically, this document clears the specification and it lets you use expressions inside decltype and sizeof.

Example:

template <class T> auto f(T t1, T t2) -> decltype(t1 + t2);

In the above case, the expression of t1+t2 needs to be checked. It will work for two int’s (the return type of the + operator is still int), but not for int and std::vector.

Expression checking adds more complexity into the compiler. In the section about overload resolution I mentioned only about doing a simple substitution for a template parameter. But now, the compiler needs to look at expressions and perform full semantic checking.

BTW: VS2013 and VS2015 support this feature only partially (msdn blog post about updates in VS 2015 update 1), some expressions might work, some (probably more complicated) might not. Clang (since 2.9) and GCC (since 4.4) fully handle “Expression SFINAE”.

Any disadvantages?

SFINAE, enable_if are very powerful features but also it’s hard to get it right. Simple examples might work, but in real-life scenarios you might get into all sorts of problems:

  • Template errors: do you like reading template errors generated by compiler? especially when you use STL types?
  • Readability
  • Nested templates usually won’t work in enable_if statements

Here is a discussion at StackOverlow: Why should I avoid std::enable_if in function signatures

Alternatives to SFINAE

  • tag dispatching - This is a much more readable version of selecting which version of a function is called. First, we define a core function and then we call version A or B depending on some compile time condition.
  • static_if - D language has this feature (see it here), but in C++ we might use a bit more complicated syntax to get similar outcome.
  • concepts (in the near future hopefully!) - All of the mentioned solutions are sort of a hack. Concepts give explicit way to express what are the requirements for a type that is accepted by a method. Still, you can try it in GCC trunk using Concepts lite implementation

One Example

To conclude my notes it would be nice to go through some working example and see how SFINAE is utilized:

Link to online compiler, coliru

The test class:

template <typename T>
class HasToString
{
private:
    typedef char YesType[1];
    typedef char NoType[2];

    template <typename C> static YesType& test( decltype(&C::ToString) ) ;
    template <typename C> static NoType& test(...);


public:
    enum { value = sizeof(test<T>(0)) == sizeof(YesType) };
};

The above template class will be used to test if some given type T has ToString() method or not. What we have here… and where is the SFINAE concept used? Can you see it?

When we want to perform the test we need to write:

HasToString<T>::value

What happens if we pass int there? It will be similar to our first example from the beginning of the article. The compiler will try to perform template substitution and it will fail on:

template <typename C> static YesType& test( decltype(&C::ToString) ) ;

Obviously, there is no int::ToString method, so the first overloaded method will be excluded from the resolution set. But then, the second method will pass (NoType& test(...)), because it can be called on all the other types. So here we get SFINAE! one method was removed and only the second was valid for this type.

In the end the final enum value, computed as:

enum { value = sizeof(test<T>(0)) == sizeof(YesType) };

returns NoType and since sizeof(NoType) is different than sizeof(YesType) the final value will be 0.

What will happen if we provide and test the following class?

class ClassWithToString
{
public:
    string ToString() { return "ClassWithToString object"; }
};

Now, the template substitution will generate two candidates: both test methods are valid, but the first one is better and that will be ‘used‘. We’ll get the YesType and finally the HasToString<ClassWithToString>::value returns 1 as the result.

How to use such checker class?

Ideally it would be handy to write some if statement:

if (HasToString<decltype(obj)>::value)
    return obj.ToString();
else
    return "undefined";

Unfortunately, all the time we’re talking about compile time checks so we cannot write such if. However, we can use enable_if and create two functions: one that will accept classes with ToString and one that accepts all other cases.

template<typename T> 
typename enable_if<HasToString<T>::value, string>::type
CallToString(T * t) {
    return t->ToString();
}

string CallToString(...)
{
    return "undefined...";
}

Again, there is SFINAE in the code above. enable_if will fail to instantiate when you pass a type that generates HasToString<T>::value = false.

Open questions: how to restrict the return type of the ToString method? and the full signature actually… ?

Things to remember:
SFINAE works in compile time, when template substitution happens, and allows to control overload resolution set for a function.

Summary

In this post, I showed a bit of theory behind SFINAE. With this technique (plus with enable_if), you can create specialized functions that will work on subset of types. SFINAE can control overloaded resolution set. Probably most of us do not need to use SFINAE on a daily basis. Still, it’s useful to know general rules behind it.

Some questions

Where do you use SFINAE and enable_if?

If you have nice example of it, please let me know and share your experience!

References

Thanks for comments: @reddit/cpp thread

Interested in new blog posts and bonus content? Sign up for my newsletter.

© 2017, Bartlomiej Filipek, Blogger platform
Any opinions expressed herein are in no way representative of those of my employers.
This site contains ads or referral links, which provide me with a commission. Thank you for your understanding.