MarketSubstitution failure is not an error
Company Profile

Substitution failure is not an error

Substitution failure is not an error (SFINAE) is a principle 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.

Example
The following example illustrates a basic instance of SFINAE: struct Test { using Foo = int; }; // Definition #1 template void f(typename T::Foo) { // ... } // Definition #2 template void f(T) { // ... } int main() { f(10); // Call #1. f(10); // Call #2. Without error (even though there is no int::Foo) due to SFINAE. return 0; } Here, attempting to use a non-class type in a qualified name (T::Foo) results in a deduction failure for f because int has no nested type named Foo, but the program is well-formed because a valid function remains in the set of candidate functions. Although SFINAE was initially introduced to avoid creating ill-formed programs when unrelated template declarations were visible (e.g., through the inclusion of a header file), many developers later found the behavior useful for compile-time introspection. Specifically, it allows a template to determine certain properties of its template arguments at instantiation time. For example, SFINAE can be used to determine if a type contains a certain typedef: import std; template struct HasTypedefFoobar { // Types "Yes" and "No" are guaranteed to have different sizes, // specifically sizeof(Yes) == 1 and sizeof(No) == 2. using Yes = char[1]; using No = char[2]; template static Yes& test(typename C::Foobar*); template static No& test(...); // If the "sizeof" of the result of calling test(nullptr) is equal to // sizeof(Yes), the first overload worked and T has a nested type named // Foobar. static constexpr bool value = (sizeof(test(nullptr)) == sizeof(Yes)); }; struct Foo { using Foobar = float; }; int main() { std::println("{}", HasTypedefFoobar::value); std::println("{}", HasTypedefFoobar::value); return 0; } When T has the nested type Foobar defined, the instantiation of the first test works and the null pointer constant is successfully passed. (And the resulting type of the expression is Yes.) If it does not work, the only available function is the second test, and the resulting type of the expression is No. An ellipsis is used not only because it will accept any argument, but also because its conversion rank is lowest, so a call to the first function will be preferred if it is possible; this removes ambiguity. == C++11 simplification ==
C++11 simplification
In C++11, the above code could be simplified to: import std; template struct HasTypedefFoobar : std::false_type {}; template struct HasTypedefFoobar> : std::true_type {}; struct Foo { using Foobar = float; }; int main() { std::println("{}", HasTypedefFoobar::value); std::println("{}", HasTypedefFoobar::value); return 0; } With the standardisation of the detection idiom in the Library fundamental v2 (n4562) proposal, the above code could be re-written as follows: import std; template using HasTypedefFoobarUnderlying = typename T::Foobar; struct Foo { using Foobar = float; }; int main() { std::println("{}", std::is_detected::value); std::println("{}", std::is_detected::value); return 0; } The developers of Boost used SFINAE in boost::enable_if and in other ways. ==References==
tickerdossier.comtickerdossier.substack.com