Content deleted Content added
m →Example |
No edit summary |
||
(56 intermediate revisions by 46 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 | coauthors=Nicolai M. Josuttis | title=C++ Templates: The Complete Guide | publisher=Addison-Wesley Professional | year=2002 | isbn=0-201-73484-2}}</ref>▼
{{Use dmy dates|date=December 2023}}
▲'''Substitution failure is not an error''' ('''SFINAE''')
Specifically, when creating a candidate set for [[overload resolution]], some (or all) candidates of that set may be the result of
==Example==
The following example illustrates a basic instance of SFINAE:
<
struct Test {
▲ typedef int Type;
};
template <
void f(typename T::
template <
void f(T) {}
f<Test>(10); // Call #1.
return 0;
}
</syntaxhighlight>
Here, attempting to use a non-class type in a qualified name (<code>T::
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 <syntaxhighlight lang="cpp">
#include <iostream>
template <typename T>
struct HasTypedefFoobar {
// Types "yes" and "no" are guaranteed to have different sizes,
// specifically sizeof(yes) == 1 and sizeof(no) == 2.
typedef char yes[1];
typedef char no[2];
template <typename C>
static yes& test(typename C::foobar*);
template <typename>
static no& test(...);
// If the "sizeof" of the result of calling test<T>(nullptr) is equal to
// sizeof(yes), the first overload worked and T has a nested type named
// foobar.
static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
};
struct Foo {
typedef float foobar;
};
int main() {
std::cout << std::boolalpha;
std::cout << HasTypedefFoobar<int>::value << std::endl; // Prints false
std::cout << HasTypedefFoobar<Foo>::value << std::endl; // Prints true
return 0;
}
</syntaxhighlight>
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.
== C++11 simplification ==
In [[C++11]], the above code could be simplified to:
<syntaxhighlight lang="cpp">
#include <iostream>
#include <type_traits>
template <typename T, typename = void>
struct HasTypedefFoobar : std::false_type {};
template <typename T>
struct HasTypedefFoobar<T, std::void_t<typename T::foobar>> : std::true_type {};
struct Foo {
using foobar = float;
};
int main() {
std::cout << std::boolalpha;
std::cout << HasTypedefFoobar<int>::value << std::endl;
std::cout << HasTypedefFoobar<Foo>::value << std::endl;
return 0;
}
</syntaxhighlight>
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:
<syntaxhighlight lang="cpp">
#include <iostream>
#include <type_traits>
template <typename T>
using HasTypedefFoobarUnderlying = typename T::foobar;
struct Foo {
using foobar = float;
};
int main() {
std::cout << std::boolalpha;
std::cout << std::is_detected<HasTypedefFoobarUnderlying, int>::value << std::endl;
std::cout << std::is_detected<HasTypedefFoobarUnderlying, Foo>::value << std::endl;
return 0;
}
</syntaxhighlight>
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.
==References==
{{reflist}}
{{C++ programming language}}
[[Category:C++]]
[[Category:Articles with example C++ code]]
[[Category:Software design patterns]]
|