- A+

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