SFINAE - cppreference.com

From cppreference.com

"Substitution Failure Is Not An Error"

This rule applies during overload resolution of function templates: When

substituting

the explicitly specified or

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.

Explanation

Function template parameters are substituted (replaced by template arguments) twice:

explicitly specified template arguments are substituted before template argument deduction

deduced arguments and the arguments obtained from the defaults are substituted after template argument deduction

Substitution occurs in

all types used in the function type (which includes return type and the types of all parameters)

all types used in the template parameter declarations

all types used in the template argument list of a partial specialization

all expressions used in the function type

all expressions used in a template parameter declaration

all expressions used in the template argument list of a partial specialization

(since C++11)A substitution failure is any situation when the type or expression above would be ill-formed (with a required diagnostic), if written using the substituted arguments.

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. A

lambda expression

is not considered part of the immediate context.(since C++20)

Substitution proceeds in lexical order and stops when a failure is encountered.

If there are multiple declarations with different lexical orders (e.g. a function template declared with trailing return type, to be substituted after a parameter, and redeclared with ordinary return type that would be substituted before the parameter), and that would cause template instantiations to occur in a different order or not at all, then the program is ill-formed; no diagnostic required.

(since C++11)template<typenameA>structB{usingtype=typenameA::type;};template<classT,classU=typenameT::type,// SFINAE failure if T has no member typeclassV=typenameB<T>::type>// hard error if B has no member type// (guaranteed to not occur via CWG 1227 because// substitution into the default template argument// of U would fail first)voidfoo(int);template<classT>typenameT::typeh(typenameB<T>::type);template<classT>autoh(typenameB<T>::type)->typenameT::type;// redeclarationtemplate<classT>voidh(...){}usingR=decltype(h<int>(0));// ill-formed, no diagnostic requiredType SFINAE

The following type errors are SFINAE errors:

attempting to instantiate a pack expansion containing multiple packs of different lengths

(since C++11)attempting to create an array of void, array of reference, array of function, array of negative size, array of non-integral size, or array of size zero:

template<intI>voiddiv(char(*)[I%2==0]=nullptr){// this overload is selected when I is even}template<intI>voiddiv(char(*)[I%2==1]=nullptr){// this overload is selected when I is odd}attempting to use a type on the left of a scope resolution operator :: and it is not a class or enumeration:

template<classT>intf(typenameT::B*);template<classT>intf(T);inti=f<int>(0);// uses second overloadattempting to use a member of a type, where

the type does not contain the specified member

the specified member is not a type where a type is required

the specified member is not a template where a template is required

the specified member is not a non-type where a non-type is required

template<intI>structX{};template<template<classT>class>structZ{};template<classT>voidf(typenameT::Y*){}template<classT>voidg(X<T::N>*){}template<classT>voidh(Z<T::templateTT>*){}structA{};structB{intY;};structC{typedefintN;};structD{typedefintTT;};structB1{typedefintY;};structC1{staticconstintN=0;};structD1{template<typenameT>structTT{};};intmain(){// Deduction fails in each of these cases:f<A>(0);// A does not contain a member Yf<B>(0);// The Y member of B is not a typeg<C>(0);// The N member of C is not a non-typeh<D>(0);// The TT member of D is not a template// Deduction succeeds in each of these cases:f<B1>(0);g<C1>(0);h<D1>(0);}// todo: needs to demonstrate overload resolution, not just failureattempting to create a pointer to reference

attempting to create a reference to void

attempting to create pointer to member of T, where T is not a class type:

template<typenameT>classis_class{typedefcharyes[1];typedefcharno[2];template<typenameC>staticyes&test(intC::*);// selected if C is a class typetemplate<typenameC>staticno&test(...);// selected otherwisepublic:staticboolconstvalue=sizeof(test<T>(nullptr))==sizeof(yes);};attempting to give an invalid type to a non-type template parameter:

template<classT,T>structS{};template<classT>intf(S<T,T()>*);structX{};inti0=f<X>(0);// todo: needs to demonstrate overload resolution, not just failureattempting to perform an invalid conversion in

in a template argument expression

in an expression used in function declaration:

template<classT,T*>intf(int);inti2=f<int,1>(0);// can’t conv 1 to int*// todo: needs to demonstrate overload resolution, not just failureattempting to create a function type with a parameter of type void

attempting to create a function type which returns an array type or a function type

Expression SFINAE

Only constant expressions that are used in types (such as array bounds) were required to be treated as SFINAE (and not hard errors) before C++11.

(until C++11)The following expression errors are SFINAE errors

Ill-formed expression used in a template parameter type

Ill-formed expression used in the function type:

structX{};structY{Y(X){}};// X is convertible to Ytemplate<classT>autof(Tt1,Tt2)->decltype(t1+t2);// overload #1Xf(Y,Y);// overload #2Xx1,x2;Xx3=f(x1,x2);// deduction fails on #1 (expression x1 + x2 is ill-formed)// only #2 is in the overload set, and is called(since C++11)SFINAE in partial specializations

Deduction and substitution also occur while determining whether a specialization of a class or variable(since C++14) template is generated by some

partial specialization

or the primary template. A substitution failure is not treated as a hard-error during such determination, but makes the corresponding partial specialization declaration ignored instead, as if in the overload resolution involving function templates.

// primary template handles non-referenceable types:template<classT,class=void>structreference_traits{usingadd_lref=T;usingadd_rref=T;};// specialization recognizes referenceable types:template<classT>structreference_traits<T,std::void_t<T&>>{usingadd_lref=T&;usingadd_rref=T&&;};template<classT>usingadd_lvalue_reference_t=typenamereference_traits<T>::add_lref;template<classT>usingadd_rvalue_reference_t=typenamereference_traits<T>::add_rref;Library support

The standard library component

std::enable_if

allows for creating a substitution failure in order to enable or disable particular overloads based on a condition evaluated at compile time.

In addition, many

type traits

must be implemented with SFINAE if appropriate compiler extensions are unavailable.

(since C++11)The standard library component

std::void_t

is another utility metafunction that simplifies partial specialization SFINAE applications.

(since C++17)Alternatives

Where applicable,

tag dispatch

,

if constexpr

(since C++17), and

concepts

(since C++20) are usually preferred over use of SFINAE.

static_assert

is usually preferred over SFINAE if only a conditional compile time error is wanted.

(since C++11)Examples

A common idiom is to use expression SFINAE on the return type, where the expression uses the comma operator, whose left subexpression is the one that is being examined (cast to void to ensure the user-defined operator comma on the returned type is not selected), and the right subexpression has the type that the function is supposed to return.

Run this code

#include<iostream>// This overload is added to the set of overloads if C is// a class or reference-to-class type and F is a pointer to member function of Ctemplate<classC,classF>autotest(Cc,Ff)->decltype((void)(c.*f)(),void()){std::cout<<"(1) Class/class reference overload called\n";}// This overload is added to the set of overloads if C is a// pointer-to-class type and F is a pointer to member function of Ctemplate<classC,classF>autotest(Cc,Ff)->decltype((void)((c->*f)()),void()){std::cout<<"(2) Pointer overload called\n";}// This overload is always in the set of overloads: ellipsis// parameter has the lowest ranking for overload resolutionvoidtest(...){std::cout<<"(3) Catch-all overload called\n";}intmain(){structX{voidf(){}};Xx;X&rx=x;test(x,&X::f);// (1)test(rx,&X::f);// (1), creates a copy of xtest(&x,&X::f);// (2)test(42,1337);// (3)}Output:

(1) Class/class reference overload called (1) Class/class reference overload called (2) Pointer overload called (3) Catch-all overload called Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior

CWG 295

C++98 creating cv-qualified function type
could result in substitution failure made not failure,
discarding cv-qualification

CWG 1227

C++98 the order of substitution was unspecified same as the lexical order

CWG 2054

C++98 substitution in partial specializations was not correctly specified specified

CWG 2322

C++11 declarations in different lexical orders would cause template
instantiations to occur in a different order or not at all such case is ill-formed,
no diagnostic required