SFINAE: What is happening here?

  • A+
Category:Languages

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(int argc, char *argv[]) {   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!

 


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; } 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: