I can't clearly get the grasp of what it means when one mentions that a particular function, struct or ... is SFINAE-friendly.
Would someone please explain it?
When it allows substitution failure without hard error (as static_assert
).
for example
template <typename T> void call_f(const T& t) { t.f(); }
The function is declared for all T
, even those with don't have f
, so you cannot do SFINAE on call_f<WithoutF>
as the method does exist. (Demo of non compiling code).
With following change:
template <typename T> auto call_f(const T& t) ->decltype(t.f(), void()) { t.f(); }
The method exists only for valid T. so you can use SFINAE as
template<typename T> auto call_f_if_available_impl(const T& t, int) -> decltype(call_f(t)) { call_f(t); } template<typename T> auto call_f_if_available_impl(const T& t, ...) { // Do nothing; } template<typename T> auto call_f_if_available(const T& t) { call_f_if_available_impl(t, 0); }
Note the int = 0
and ...
is to order the overload. Demo
--
An other case is when the template add special parameter to apply SFINAE for specialization:
template <typename T, typename Enabler = void> struct S;
And then
// Specialization only available for T which respect the traits. template <typename T> struct S<T, std::enable_if_t<my_type_trait<T>::value>> { };
An entity is termed SFINAE-friendly if it can be used in the context of SFINAE without producing a hard error upon substitution failure. I assume you already know what SFINAE is, as that is a whole other question in itself.
In the context of C++ standardization, the term SFINAE-friendly has so far been applied to std::result_of
and std::common_type
. Take the following example:
template <typename T> void foo(T x, typename std::common_type<T, int>::type y) {} void foo(std::string x, std::string y) {} int main() { foo(std::string("hello"), std::string("world")); }
Without SFINAE-friendly common_type
, this would fail to compile, because std::common_type<std::string, int>::type
would produce a hard error during template argument substitution. With the introduction of SFINAE-friendly common_type
(N3843) this example becomes well-formed, because std::common_type<std::string, int>::type
produces a substitution failure so that overload is excluded from the viable set.
Here's a similar example with result_of
:
template <typename T> auto bar(T f) -> typename std::result_of<T()>::type { return f(); } void bar(int n) {} int main() { bar(42); }
Without SFINAE-friendly result_of
, this would fail to compile, because std::result_of<int()>::type
would produce a hard error during template argument substitution. With the introduction of SFINAE-friendly result_of
(N3462) this example becomes well-formed, because std::result_of<int()>::type
produces a substitution failure so that overload is excluded from the viable set.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With