31

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?

Mehrdad
  • 1,186
  • 7
  • 15

2 Answers2

28

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 which 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>>
{
};
Enlico
  • 23,259
  • 6
  • 48
  • 102
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Isn't `int = 0` vs `...` ambiguous? – TartanLlama Jan 27 '16 at 09:44
  • 1
    What do you mean by: *you cannot do SFINAE on `call_f`*? – Mehrdad Jan 27 '16 at 09:49
  • 1
    @TartanLlama no it's not because `int` is __more specialized__ than `...` – tkausl Jan 27 '16 at 09:49
  • Yes, it's ambiguous. It won't compile. – Richard Hodges Jan 27 '16 at 09:52
  • @TartanLlama: Fixed, Thanks. – Jarod42 Jan 27 '16 at 09:54
  • @tkausl But the ICSs for overload resolution don't prefer either for a call which uses the default argument. [Demo](http://coliru.stacked-crooked.com/a/080986badbc23730). – TartanLlama Jan 27 '16 at 09:54
  • Thanks for examples, I've been trying them. So at the end `auto call_f_if_available(const T& t)` is SFINAE-friendly, i.e. it will compile no matter if `f()` is available or not. but `auto call_f(const T& t) ->decltype(t.f(), void())` and `void call_f(const T& t)` are **not**. Correct? Can you add this info to make it clear too? – Mehrdad Jan 27 '16 at 10:06
  • @MehrdadMomeny: `void call_f(const T& t)` is not SFINAE friendly as declaration exists even for invalid `T`. `auto call_f(const T& t) ->decltype(t.f(), void())` is friendly as for invalid `T`, the declaration won't be part of valid overload. Finally, `call_f_if_available` is valid for all `T` so it is irrelevant for Substitution failure. – Jarod42 Jan 27 '16 at 10:17
  • Since you said without hard error! I thought that call_f_if_available suites that definition better, because there's no error. – Mehrdad Jan 27 '16 at 12:50
  • With `->decltype(..)`, `call_f(WithoutF{})` is valid and error appears inside the body of the function `call_f` (too late to see that `call_f(WithoutF{})` is invalid). In the friendly version, the error is know from the declaration. – Jarod42 Jan 27 '16 at 13:01
  • 1
    This explains what SFINAE is but not what it means for something to be SFINAE-friendly (e.g. N3462 & N3843). – Oktalist Jan 27 '16 at 15:04
  • @Oktalist: I show example only for function and not for struct. but same logic apply. – Jarod42 Jan 27 '16 at 15:58
6

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.

Oktalist
  • 14,336
  • 3
  • 43
  • 63