Substitution failure is not an error: Difference between revisions

Content deleted Content added
No edit summary
No edit summary
 
(12 intermediate revisions by 10 users not shown)
Line 1:
{{Short description|C++ programming technique}}
'''Substitution failure is not an error''' ('''SFINAE''') refers to a situation in [[C++]] where an invalid substitution of [[template (programming)|template]] parameters is not in itself an error. David Vandevoorde first introduced the acronym SFINAE to describe related programming techniques.<ref>{{cite book | last=Vandevoorde | first=David |author2=Nicolai M. Josuttis | title=C++ Templates: The Complete Guide | publisher=Addison-Wesley Professional | year=2002 | isbn=0-201-73484-2}}</ref>
{{useUse dmy dates|date=JanuaryDecember 20122023}}
'''Substitution failure is not an error''' ('''SFINAE''') refers tois a situationprinciple in [[C++]] where an invalid substitution of [[templateTemplate (programmingC++)|template]] parameters is not in itself an error. David Vandevoorde first introduced the acronym SFINAE to describe related programming techniques.<ref>{{cite book | last=Vandevoorde | first=David |author2=Nicolai M. Josuttis | title=C++ Templates: The Complete Guide | publisher=Addison-Wesley Professional | year=2002 | isbn=0-201-73484-2}}</ref>
 
Specifically, when creating a candidate set for [[overload resolution]], some (or all) candidates of that set may be the result of instantiated templates with (potentially deduced) template arguments substituted for the corresponding template parameters. If an error occurs during the substitution of a set of arguments for any given template, the compiler removes the potential overload from the candidate set instead of stopping with a compilation error, provided the substitution error is onethat the C++ standard grantspermits discarding such treatmenta substitution error as mentioned.<ref>International Organization for Standardization. "ISO/IEC 14882:2003, Programming languages &mdash; C++", § 14.8.2.</ref> If one or more candidates remain and overload resolution succeeds, the invocation is well-formed.
 
==Example==
The following example illustrates a basic instance of SFINAE:
 
<sourcesyntaxhighlight lang="cpp">
struct Test {
typedef int foo;
Line 21 ⟶ 23:
f<int>(10); // Call #2. Without error (even though there is no int::foo)
// thanks to SFINAE.
return 0;
}
</syntaxhighlight>
</source>
 
Here, attempting to use a non-class type in a qualified name (<code>T::foo</code>) results in a deduction failure for <code>f<int></code> because <code>int</code> has no nested type named <code>foo</code>, but the program is well-formed because a valid function remains in the set of candidate functions.
Line 30 ⟶ 33:
For example, SFINAE can be used to determine if a type contains a certain typedef:
 
<sourcesyntaxhighlight lang="cpp">
#include <iostream>
 
template <typename T>
struct has_typedef_foobarHasTypedefFoobar {
// Types "yes" and "no" are guaranteed to have different sizes,
// specifically sizeof(yes) == 1 and sizeof(no) == 2.
Line 52 ⟶ 55:
};
 
struct fooFoo {
typedef float foobar;
};
Line 58 ⟶ 61:
int main() {
std::cout << std::boolalpha;
std::cout << has_typedef_foobarHasTypedefFoobar<int>::value << std::endl; // Prints false
std::cout << has_typedef_foobarHasTypedefFoobar<fooFoo>::value << std::endl; // Prints true
return 0;
}
</syntaxhighlight>
</source>
 
When <code>T</code> has the nested type <code>foobar</code> defined, the instantiation of the first <code>test</code> works and the null pointer constant is successfully passed. (And the resulting type of the expression is <code>yes</code>.) If it does not work, the only available function is the second <code>test</code>, and the resulting type of the expression is <code>no</code>. 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.
Line 68 ⟶ 72:
In [[C++11]], the above code could be simplified to:
 
<sourcesyntaxhighlight lang="cpp">
#include <iostream>
#include <type_traits>
 
template <typename... Ts>
using void_t = void;
 
template <typename T, typename = void>
struct has_typedef_foobarHasTypedefFoobar : std::false_type {};
 
template <typename T>
struct has_typedef_foobarHasTypedefFoobar<T, std::void_t<typename T::foobar>> : std::true_type {};
 
struct fooFoo {
using foobar = float;
};
Line 87 ⟶ 88:
int main() {
std::cout << std::boolalpha;
std::cout << has_typedef_foobarHasTypedefFoobar<int>::value << std::endl;
std::cout << has_typedef_foobarHasTypedefFoobar<fooFoo>::value << std::endl;
return 0;
}
</syntaxhighlight>
</source>
 
With the standardisation of the detection idiom in the [http://en.cppreference.com/w/cpp/experimental/lib_extensions_2 Library fundamental v2 (n4562)] proposal, the above code could be re-written as follows:
<sourcesyntaxhighlight lang="cpp">
#include <iostream>
#include <type_traits>
 
template <typename T>
using has_typedef_foobar_tHasTypedefFoobarUnderlying = typename T::foobar;
 
struct fooFoo {
using foobar = float;
};
Line 106 ⟶ 108:
int main() {
std::cout << std::boolalpha;
std::cout << std::is_detected<has_typedef_foobar_tHasTypedefFoobarUnderlying, int>::value << std::endl;
std::cout << std::is_detected<has_typedef_foobar_tHasTypedefFoobarUnderlying, fooFoo>::value << std::endl;
return 0;
}
</syntaxhighlight>
</source>
 
The developers of [[Boost C++ Libraries|Boost]] used SFINAE in boost::enable_if<ref name="enable_if">[http://www.boost.org/doc/libs/release/libs/utility/enable_if.html Boost Enable If]</ref> and in other ways.
Line 116 ⟶ 119:
{{reflist}}
 
{{C++ programming language}}
{{use dmy dates|date=January 2012}}
 
[[Category:C++]]
[[Category:Articles with example C++ code]]