Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C++14

Notes on C++ SFINAE

Rate me:
Please Sign up or sign in to vote.
4.38/5 (5 votes)
29 Feb 2016CPOL10 min read 11.3K   111   11  
In the article I cover SFINAE, a quite complex paradigm from C++ template programming area. What is this thing? Where can you use it? Let's also look how C++14/17 can help in writing such expressions.

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:

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

C++
<code>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!
}
</code>

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):

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

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

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

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:

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

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:

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

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:

<code>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) };
};
</code>

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:

<code>HasToString<T>::value
</code>

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:

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

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:

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

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?

<code>class ClassWithToString
{
public:
    string ToString() { return "ClassWithToString object"; }
};
</code>

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:

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

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.

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

string CallToString(...)
{
    return "undefined...";
}
</code>

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.

Example Improvements

The above example was using some standard, one may even say - old - techniques. We can update this a bit, and even improve (with exact return type checking).

If I am correct and assuming you have void_t in your compiler/library, this is a new version of the code:

<code>// default template:
template< class , class = void >
struct has_toString : false_type { };

// specialized as has_member< T , void > or sfinae
template< class T >
struct has_toString< T , void_t<decltype(&T::toString) > > : std::is_same<std::string, decltype(declval<T>().toString())>
{ };
</code>

http://melpon.org/wandbox/permlink/ZzSz25GJVaY4cvzw

Pretty nice… right? :)

It uses explicit detection idiom based on void_t. Basically, when there is no T::toString() in the class, SFINAE happens and we end up with the general, default template (and thus with false_type). But when there is such method in the class, the specialized version of the template is chosen. This could be the end if we don’t care about the return type of the method. But in this version we check this by inheriting from std::is_same. The code checks if the return type of the method is std::string. Then we can end up with true_type or false_type.

For more information about void_t please watch amazing video by Walter E. Brown:

CppCon 2014: Walter E. Brown "Modern Template Metaprogramming: A Compendium, Part II"

Starting at around 29 minute, and especially around 39 minute.

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

Books:

History

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Poland Poland
Software developer interested in creating great code and passionate about teaching.

Author of C++17 In Detail - a book that will teach you the latest features of C++17!

I have around 11 years of professional experience in C++/Windows/Visual Studio programming. Plus other technologies like: OpenGL, game development, performance optimization.

In 2018 I was awarded by Microsoft as MVP, Developer Technologies.

If you like my articles please subscribe to my weekly C++ blog or just visit www.bfilipek.com.

Comments and Discussions

 
-- There are no messages in this forum --