Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SFINAE: What is happening here?

Tags:

c++

I am currently trying to understand a C++ code, and have come across SFINAE construct (which is new to me). I have created a minimal example, based on the code I am looking at below:

#include<iostream>

/* ----------------------------------------------
 Define two kernels: characterized by their dimension
   ---------------------------------------------- */
struct Kern2
{
  static constexpr int dim = 2;
};

struct Kern3
{
  static constexpr int dim = 3;
};

/* ----------------------------------------------
 Choose which function to evaluate based on 
 dimension of Kern (Kern::dim)
   ---------------------------------------------- */
template<class Kern,
         typename std::enable_if<Kern::dim == 2, bool>::type = true>
inline void apply_kern(){
  std::cout << "dim=2" << "\n";
}

template<class Kern,
         typename std::enable_if<Kern::dim == 3, bool>::type = false>
inline void apply_kern(){
  std::cout << "dim=3" << "\n";
}

// Try to see if the above SFINAE construct works!
int main()
{

 apply_kern<Kern2>(); // should print 'dim=2'
 apply_kern<Kern3>(); // should print 'dim=3'

  return 0;
}

This gives as output:

> dim=2
> dim=3

which is exactly what it's supposed to do. However, I am unable to understand exactly how this works? In particular, it appears that the same output is created if I switch the

typename std::enable_if<Kern::dim == 2, bool>::type = true

lines to:

typename std::enable_if<Kern::dim == 2, bool>::type = false

So I'm wondering what the meaning of these is? If someone could kindly explain what's going on, I'd greatly appreciate it! I haven't been able to find this precise way to use SFINAE online, unfortunately.

Thanks!

like image 550
Sam Avatar asked Dec 07 '18 11:12

Sam


People also ask

What is meant by SFINAE?

Substitution failure is not an error (SFINAE) refers to a situation in C++ where an invalid substitution of template parameters is not in itself an error. David Vandevoorde first introduced the acronym SFINAE to describe related programming techniques.

What is Typename C++?

" typename " is a keyword in the C++ programming language used when writing templates. It is used for specifying that a dependent name in a template definition or declaration is a type.


1 Answers

typename std::enable_if<Kern::dim == 2, bool>::type = true>

That says:

typename:

the following term defines a type

std::enable_if<Kern::dim == 2, bool>

This template defines a type of the second template parameter IF the condition in the first parameter is true. So here, if dimm == 2 is true, the template std::enable_if provide a type bool which can be accessed with the ::type.

If the condition was true, the term:

typename std::enable_if<Kern::dim == 3, bool>::type

becomes simply:

bool

Now you add = true after it. Did you use the bool value anywhere? NO! So it simply doesn't matter at all! you also can write:

typename std::enable_if<Kern::dim == 3, int>::type = 42

It will result in the same, as you did not use the value you define here!

The condition you check is in Kern::dim == 3. This one must be true or false.

If the condition is evaluated to false, the template enable_if did not contain a type and the expression fails. Here SFINAE comes into play. This failure will not be an error but makes the template definition "invisible" as it "can not" be used cause of the failure.

Add-On for the addition question in the comments:

Sure, you can add a name to your bool template default paramter and use it in your code below like this:

template<class Kern,
         typename std::enable_if<Kern::dim == 2, bool>::type myVal = true>
inline void apply_kern(){
  std::cout << "dim=2" << "\n";
  std::cout << "bool val: " << myVal << std::endl;
}

BTW: We often see SFINAE used in cases, where a simple template overload works the same way. Often the overload is easier to read ( here maybe not :-) ). I give it only as a hint: Check if SFINAE is really needed and think of a overload instead.

Template overload instead of SFINAE:

/* ----------------------------------------------
   Define two kernels: characterized by their dimension
   ---------------------------------------------- */
struct Kern2 { static constexpr int dim = 2; };
struct Kern3 { static constexpr int dim = 3; };

/* ----------------------------------------------
   Choose which function to evaluate based on 
   dimension of Kern (Kern::dim)
   ---------------------------------------------- */
template < int x > inline void apply_kern_impl();

template<>
inline void apply_kern_impl<2>() { std::cout << "dim=2" << "\n"; }

template<>
inline void apply_kern_impl<3>() { std::cout << "dim=3" << "\n"; }

template< typename T>
inline void apply_kern() { apply_kern_impl<T::dim>(); }

int main()
{
    apply_kern<Kern2>(); // should print 'dim=2'
    apply_kern<Kern3>(); // should print 'dim=3'

    return 0;
}
like image 176
Klaus Avatar answered Oct 05 '22 13:10

Klaus