Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does it mean when one says something is SFINAE-friendly?

Tags:

c++

sfinae

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?

like image 514
Mehrdad Avatar asked Jan 27 '16 09:01

Mehrdad


2 Answers

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>> { }; 
like image 66
Jarod42 Avatar answered Sep 24 '22 11:09

Jarod42


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.

like image 33
Oktalist Avatar answered Sep 21 '22 11:09

Oktalist