Template specialization for SFINAE

sfinae constructor
expression sfinae
no type named 'type' in enable_if
c sfinae member function
sfinae-friendly
sfinae lambda
sfinae function overloading
c++ template restrict type

I have been working with templates for quite some time now but recently I encountered a weird template programming for SFINAE. My question is why do we write typename = {something} as a template parameter in C++?


That is simply how SFINAE works ;)

As you know, you have to "create a failure" within the template declaration and not inside the template definition. As this:

template < typename X, typename = ... here is the code which may generate an error     during instantiation >
void Bla() {}

The only chance to put some "code" in the declaration is to define something in the template parameter list or inside the template function declaration itself like:

template < typename X>
void Bla( ... something which may generates an error ) {}

Example:

template <typename T, typename = std::enable_if_t< std::is_same_v< int, T>>>
void Bla()
{
    std::cout << "with int" << std::endl;
}

template <typename T, typename = std::enable_if_t< !std::is_same_v< int, T>>>
void Bla(int=0)
{
    std::cout << "with something else" << std::endl;
}

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

But what is the background of "creating an substitution failure" here?

The trick is somewhere behind std::enable_if. We can also use it without that:

template <typename T, typename = char[ std::is_same_v< int, T>]>
void Bla()
{   
    std::cout << "with int" << std::endl;
}   

template <typename T, typename = char[ !std::is_same_v< int, T>]>
void Bla(int=0)
{   
    std::cout << "with something else" << std::endl;
}   

Take a look on: typename = char[ !std::is_same_v< int, T>] Here std::is_same_v gives us a bool value back which is casted to 0 if not valid and to any postive number if valid. And creating a char[0] is simply an error in c++! So with the negation of our expression with ! we got one for "is int" and one for "is not int". Once it tries to create an array of size 0, which is a failure and the template will not be instantiated and once it generates the type char[!0] which is a valid expression.

And as the last step:

... typename = ...

is meant as: there will be defined a type, here without a template parameter name, as the parameter itself is not used later. You also can write:

... typename X = ... 

but as X is not used, leave it!

Summary: You have to provide some code, which generates an error or not, depending on the type or value of a given expression. The expression must be part of the function declaration or part of the template parameter list. It is not allowed to put an error inside the function/class definition, as this will not longer be "not an error" in the sense of SFINAE.

Update: Can the results of SFINAE expressions be used for further expressions: Yes

Example:

    template < typename TYPE >
void CheckForType()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

    template <typename T, typename X = std::enable_if_t< std::is_same_v< int, T>, float>>
void Bla()
{
    std::cout << "with int" << std::endl;
    CheckForType<X>();
}

    template <typename T, typename X = std::enable_if_t< !std::is_same_v< int, T>, double >>
void Bla(int=0)
{
    std::cout << "with something else" << std::endl;
    CheckForType<X>();
}

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

Output:

with int
void CheckForType() [with TYPE = float]
with something else
void CheckForType() [with TYPE = double]

Explanation: std::enable_if has a second template parameter which is used as return type, if the first parameter of it will be true. As this, you can use that type here. As you can see, the function CheckForType is called with this defined type for X from the SFINAE expression.

Class template SFINAE, Conditionally instantiate a class template depending on the template arguments. Description. We provide two partial specializations of the foo  Only the failures in the types and expressions in the immediate context of the function type or its template parameter types or its explicit specifier (since C++20) are SFINAE errors. If the evaluation of a substituted type/expression causes a side-effect such as instantiation of some template specialization, generation of an implicitly-defined member function, etc, errors in those side-effects are treated as hard errors.


An example :

//for enum types.
template <typename T, typename std::enable_if<std::is_enum_v<T>, int>::type = 0>
void Foo(T value)
{
  //stuff..
}

I only want this Foo to be picked if T is an enum type. We can achieve this using the rule of Substitute Failure Is Not An Error (SFINAE).


This rule states that if there's a failure during substitution this shouldn't be a compilation error. The compiler should only eliminate the function for this substitution.

So then, how do we make sure Foo is only called with an enum type? Well that's easy, we find a way to trigger a substitution failure!

If we check cppreference for std::enable_if it says:

template< bool B, class T = void >
struct enable_if;

If B is true, std::enable_if has a public member typedef type, equal to T; otherwise, there is no member typedef.

This means, if std::is_enum<T>::value is true (which is the case for enum types for T) then B will be true and thus type will be a valid type, an int as specified.

If however std::is_enum::value is false then B will be false and thus type won't even exist. In this case the code is trying to use ::type while it doesn't exist and thus that's a substitution error. So SFINAE kicks in and eliminates it from the candidate list.


The reason we use = 0 is to provide a default template parameter value since we're not actually interested in using this type or its value, we only use it to trigger SFINAE.

SFINAE - cppreference.com, Today's post is about template SFINAE & type-traits - cool C++ partial specialization for when Condition==true template <typename T> struct  Alias templates are a new way to do typedefs in C++11. You have probably seen them by now, but as a reminder here is what the standard considers to be an alias-declaration: So that's cool. Unfortunately the standard also says that "Because an alias-declaration cannot declare a template-id, it is not possible to partially or…


This construct is a nameless template parameter supplied with a default argument. Normally a template parameter would look like

typename <identifier> [ = <type> ]

but the identifier may be omitted.

You can see this construction often in SFINAE templates because it's a convenient way to enable a specialisation if and only if some condition holds, or if some piece of code is valid. Thus one can often see

template <typename T, typename = std::enable_if<(some-constexpr-involving-T)>::type> ...

If the expression happens to evaluate to true, all is well: std::enable_if<true>::type is just void, and we have a good working template specialisation.

Now if the expression above happens to be false, std::enable_if<false> doesn't have a member named type, Substitution Failure (the SF in SFINAE) happens, and the template specialisation is not considered.

And here the role of tge parameter ends. It is not used for anything in the template itself, so it doesn't need a name.

Template SFINAE & type-traits, The answer is "template 42", and the reason for this is that integer literals the specialization of struct enable_if for true is used, and its internal  Template SFINAE SFINAE is a scary-looking C++ acronym, which joins a long list of hard-to-remember capital-letter concepts (such as RAII, RVO, RTTI, PIMPL, etc). SFINAE stands for “Substitution Failure In Not An Error”.


SFINAE and enable_if, classic “class template specialization,” which we all know because of parameter is to give the client a place to hang SFINAE constraints. An explicit specialization of a function template is inline only if it is declared with the inline specifier (or defined as deleted), it doesn't matter if the primary template is inline. Default function arguments cannot be specified in explicit specializations of function templates, member function templates, and member functions of class templates when the class is implicitly instantiated. An explicit specialization cannot be a friend declaration.


This one weird trick for customization by class template (partial , SFINAE. This rule applies during overload resolution of function templates: When substituting the deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error. This feature is used in template metaprogramming. The template on lines 6–8 will only be instantiated when T is an integral type. The template on lines 10–12 will only be instantiated when T is a floating point type. This allows us to provide different implementations of the foo class depending on the template arguments it is instantiated with.


Notes on C++ SFINAE, Modern C++ and C++ , Explicit specializations are not exclusive to function templates, but for function templates using a combination of overloading and SFINAE  If a partial specialization of the member template is explicitly specialized for a given (implicit) specialization of the enclosing class template, the primary member template and its other partial specializations are still considered for this specialization of the enclosing class template.