SFINAE: What is happening here?











up vote
13
down vote

favorite
3












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!










share|improve this question




















  • 1




    With the mentioned modification, your code shouldn't compile.
    – geza
    Dec 7 at 11:44










  • Both g++ and clang++ accepts the change (-std=c++11/14/17).
    – Ted Lyngmo
    Dec 7 at 11:48






  • 1




    @TedLyngmo: did I miss something? godbolt.org/z/wbvn7I
    – geza
    Dec 7 at 11:55






  • 3




    The =true or =false is not a comparison, it is just the default value for the template parameter if it is omitted. The SFINAE magic is done inside std::enable_if, not in the =true/false.
    – Werner Henze
    Dec 7 at 11:56






  • 3




    There is a nice introduction written by Eli Bendersky which I just consulted these days: SFINAE and enable_if.
    – Scheff
    Dec 7 at 12:00

















up vote
13
down vote

favorite
3












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!










share|improve this question




















  • 1




    With the mentioned modification, your code shouldn't compile.
    – geza
    Dec 7 at 11:44










  • Both g++ and clang++ accepts the change (-std=c++11/14/17).
    – Ted Lyngmo
    Dec 7 at 11:48






  • 1




    @TedLyngmo: did I miss something? godbolt.org/z/wbvn7I
    – geza
    Dec 7 at 11:55






  • 3




    The =true or =false is not a comparison, it is just the default value for the template parameter if it is omitted. The SFINAE magic is done inside std::enable_if, not in the =true/false.
    – Werner Henze
    Dec 7 at 11:56






  • 3




    There is a nice introduction written by Eli Bendersky which I just consulted these days: SFINAE and enable_if.
    – Scheff
    Dec 7 at 12:00















up vote
13
down vote

favorite
3









up vote
13
down vote

favorite
3






3





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!










share|improve this question















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!







c++






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 8 at 15:19

























asked Dec 7 at 11:28









Sam

1746




1746








  • 1




    With the mentioned modification, your code shouldn't compile.
    – geza
    Dec 7 at 11:44










  • Both g++ and clang++ accepts the change (-std=c++11/14/17).
    – Ted Lyngmo
    Dec 7 at 11:48






  • 1




    @TedLyngmo: did I miss something? godbolt.org/z/wbvn7I
    – geza
    Dec 7 at 11:55






  • 3




    The =true or =false is not a comparison, it is just the default value for the template parameter if it is omitted. The SFINAE magic is done inside std::enable_if, not in the =true/false.
    – Werner Henze
    Dec 7 at 11:56






  • 3




    There is a nice introduction written by Eli Bendersky which I just consulted these days: SFINAE and enable_if.
    – Scheff
    Dec 7 at 12:00
















  • 1




    With the mentioned modification, your code shouldn't compile.
    – geza
    Dec 7 at 11:44










  • Both g++ and clang++ accepts the change (-std=c++11/14/17).
    – Ted Lyngmo
    Dec 7 at 11:48






  • 1




    @TedLyngmo: did I miss something? godbolt.org/z/wbvn7I
    – geza
    Dec 7 at 11:55






  • 3




    The =true or =false is not a comparison, it is just the default value for the template parameter if it is omitted. The SFINAE magic is done inside std::enable_if, not in the =true/false.
    – Werner Henze
    Dec 7 at 11:56






  • 3




    There is a nice introduction written by Eli Bendersky which I just consulted these days: SFINAE and enable_if.
    – Scheff
    Dec 7 at 12:00










1




1




With the mentioned modification, your code shouldn't compile.
– geza
Dec 7 at 11:44




With the mentioned modification, your code shouldn't compile.
– geza
Dec 7 at 11:44












Both g++ and clang++ accepts the change (-std=c++11/14/17).
– Ted Lyngmo
Dec 7 at 11:48




Both g++ and clang++ accepts the change (-std=c++11/14/17).
– Ted Lyngmo
Dec 7 at 11:48




1




1




@TedLyngmo: did I miss something? godbolt.org/z/wbvn7I
– geza
Dec 7 at 11:55




@TedLyngmo: did I miss something? godbolt.org/z/wbvn7I
– geza
Dec 7 at 11:55




3




3




The =true or =false is not a comparison, it is just the default value for the template parameter if it is omitted. The SFINAE magic is done inside std::enable_if, not in the =true/false.
– Werner Henze
Dec 7 at 11:56




The =true or =false is not a comparison, it is just the default value for the template parameter if it is omitted. The SFINAE magic is done inside std::enable_if, not in the =true/false.
– Werner Henze
Dec 7 at 11:56




3




3




There is a nice introduction written by Eli Bendersky which I just consulted these days: SFINAE and enable_if.
– Scheff
Dec 7 at 12:00






There is a nice introduction written by Eli Bendersky which I just consulted these days: SFINAE and enable_if.
– Scheff
Dec 7 at 12:00














2 Answers
2






active

oldest

votes

















up vote
15
down vote



accepted










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





share|improve this answer



















  • 2




    Great start. Now we need to explain how it works when the condition is false :)
    – Lightness Races in Orbit
    Dec 7 at 12:03






  • 1




    @Sam Yes, you would just have to put a name between ::type and =. However, you don't need to use it in the function (it's only there for SFINAE) and the default value means that users don't have to write apply_kern<Kern2, true>(); (or false, it does not matter) but only apply_kern<Kern2>().
    – Max Langhof
    Dec 7 at 12:11








  • 1




    @Sam: Added example for using the template default var...
    – Klaus
    Dec 7 at 12:13






  • 2




    This is now a fantastic answer all around.
    – Lightness Races in Orbit
    Dec 7 at 13:03






  • 2




    This answer taught me more about enable_if than I managed to learn after hours of reading books and searching online for explicit examples.
    – pipe
    Dec 7 at 14:40


















up vote
1
down vote













std::enable_if<bool expression, return type of function>::type tells the compiler at compile time, compile this if the bool expression is true. So when you call the apply_kern<Kern2>() in your main() the compiler enters the first std::enable_if because you Kern::dim is indeed 2. If for example you did not call apply_kern<Kern3>(), the compiler would notice that the second std::enable_if was false and the scope inside it was not gonna get compiled. It's like an if statement, but in compile time. You could also use a templated function with 2 template aliases for Kern2 and Kern3 respectively and have the same exact result if you find this syntax strange.
For the same result in the last question i'd try typename std::enable_if<!(Kern::dim == 2), bool>::type inline void apply_kernel(){...}.






share|improve this answer





















  • @LeoStar: Thank you for your answer! So the point is simply to provide the compile with some expression that will fail, except if we are in the desired case? Is that right? In particular, there is no deeper meaning to the use of the particular construct employed in the code that I'm looking at?
    – Sam
    Dec 7 at 12:10






  • 1




    @Sam Correct. The type and default value chosen there are irrelevant, they remain unused. The entire purpose is to provide no type if the condition is not fulfilled, which leads to substitution failure (and thus ignoring that template). The default value is for convenience of the user (if you remove the default value everything still works, the user would just have to add a meaningless value to the template parameter list when calling apply_kern).
    – Max Langhof
    Dec 7 at 12:15












  • @Sam Actually you need only to write the expression that you want your compiler to compile. In the other cases in compile time the bool expression inside the std::enable_if is gonna return false and this way the function in not gonna get compiled. I would think it like the name of the enable_if: Write all the situations in which you want to get the compiler compile this. It's just a matter or implementation, but with the right bool expressions inside enable_if. If you add an extra check inside the std::enable_if like f.e. std::is_same, it gains a deeper meaning fir template metaprogramming
    – LeoSar
    Dec 7 at 12:22











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53668647%2fsfinae-what-is-happening-here%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
15
down vote



accepted










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





share|improve this answer



















  • 2




    Great start. Now we need to explain how it works when the condition is false :)
    – Lightness Races in Orbit
    Dec 7 at 12:03






  • 1




    @Sam Yes, you would just have to put a name between ::type and =. However, you don't need to use it in the function (it's only there for SFINAE) and the default value means that users don't have to write apply_kern<Kern2, true>(); (or false, it does not matter) but only apply_kern<Kern2>().
    – Max Langhof
    Dec 7 at 12:11








  • 1




    @Sam: Added example for using the template default var...
    – Klaus
    Dec 7 at 12:13






  • 2




    This is now a fantastic answer all around.
    – Lightness Races in Orbit
    Dec 7 at 13:03






  • 2




    This answer taught me more about enable_if than I managed to learn after hours of reading books and searching online for explicit examples.
    – pipe
    Dec 7 at 14:40















up vote
15
down vote



accepted










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





share|improve this answer



















  • 2




    Great start. Now we need to explain how it works when the condition is false :)
    – Lightness Races in Orbit
    Dec 7 at 12:03






  • 1




    @Sam Yes, you would just have to put a name between ::type and =. However, you don't need to use it in the function (it's only there for SFINAE) and the default value means that users don't have to write apply_kern<Kern2, true>(); (or false, it does not matter) but only apply_kern<Kern2>().
    – Max Langhof
    Dec 7 at 12:11








  • 1




    @Sam: Added example for using the template default var...
    – Klaus
    Dec 7 at 12:13






  • 2




    This is now a fantastic answer all around.
    – Lightness Races in Orbit
    Dec 7 at 13:03






  • 2




    This answer taught me more about enable_if than I managed to learn after hours of reading books and searching online for explicit examples.
    – pipe
    Dec 7 at 14:40













up vote
15
down vote



accepted







up vote
15
down vote



accepted






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





share|improve this answer














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






share|improve this answer














share|improve this answer



share|improve this answer








edited Dec 7 at 12:29

























answered Dec 7 at 12:02









Klaus

10.7k12558




10.7k12558








  • 2




    Great start. Now we need to explain how it works when the condition is false :)
    – Lightness Races in Orbit
    Dec 7 at 12:03






  • 1




    @Sam Yes, you would just have to put a name between ::type and =. However, you don't need to use it in the function (it's only there for SFINAE) and the default value means that users don't have to write apply_kern<Kern2, true>(); (or false, it does not matter) but only apply_kern<Kern2>().
    – Max Langhof
    Dec 7 at 12:11








  • 1




    @Sam: Added example for using the template default var...
    – Klaus
    Dec 7 at 12:13






  • 2




    This is now a fantastic answer all around.
    – Lightness Races in Orbit
    Dec 7 at 13:03






  • 2




    This answer taught me more about enable_if than I managed to learn after hours of reading books and searching online for explicit examples.
    – pipe
    Dec 7 at 14:40














  • 2




    Great start. Now we need to explain how it works when the condition is false :)
    – Lightness Races in Orbit
    Dec 7 at 12:03






  • 1




    @Sam Yes, you would just have to put a name between ::type and =. However, you don't need to use it in the function (it's only there for SFINAE) and the default value means that users don't have to write apply_kern<Kern2, true>(); (or false, it does not matter) but only apply_kern<Kern2>().
    – Max Langhof
    Dec 7 at 12:11








  • 1




    @Sam: Added example for using the template default var...
    – Klaus
    Dec 7 at 12:13






  • 2




    This is now a fantastic answer all around.
    – Lightness Races in Orbit
    Dec 7 at 13:03






  • 2




    This answer taught me more about enable_if than I managed to learn after hours of reading books and searching online for explicit examples.
    – pipe
    Dec 7 at 14:40








2




2




Great start. Now we need to explain how it works when the condition is false :)
– Lightness Races in Orbit
Dec 7 at 12:03




Great start. Now we need to explain how it works when the condition is false :)
– Lightness Races in Orbit
Dec 7 at 12:03




1




1




@Sam Yes, you would just have to put a name between ::type and =. However, you don't need to use it in the function (it's only there for SFINAE) and the default value means that users don't have to write apply_kern<Kern2, true>(); (or false, it does not matter) but only apply_kern<Kern2>().
– Max Langhof
Dec 7 at 12:11






@Sam Yes, you would just have to put a name between ::type and =. However, you don't need to use it in the function (it's only there for SFINAE) and the default value means that users don't have to write apply_kern<Kern2, true>(); (or false, it does not matter) but only apply_kern<Kern2>().
– Max Langhof
Dec 7 at 12:11






1




1




@Sam: Added example for using the template default var...
– Klaus
Dec 7 at 12:13




@Sam: Added example for using the template default var...
– Klaus
Dec 7 at 12:13




2




2




This is now a fantastic answer all around.
– Lightness Races in Orbit
Dec 7 at 13:03




This is now a fantastic answer all around.
– Lightness Races in Orbit
Dec 7 at 13:03




2




2




This answer taught me more about enable_if than I managed to learn after hours of reading books and searching online for explicit examples.
– pipe
Dec 7 at 14:40




This answer taught me more about enable_if than I managed to learn after hours of reading books and searching online for explicit examples.
– pipe
Dec 7 at 14:40












up vote
1
down vote













std::enable_if<bool expression, return type of function>::type tells the compiler at compile time, compile this if the bool expression is true. So when you call the apply_kern<Kern2>() in your main() the compiler enters the first std::enable_if because you Kern::dim is indeed 2. If for example you did not call apply_kern<Kern3>(), the compiler would notice that the second std::enable_if was false and the scope inside it was not gonna get compiled. It's like an if statement, but in compile time. You could also use a templated function with 2 template aliases for Kern2 and Kern3 respectively and have the same exact result if you find this syntax strange.
For the same result in the last question i'd try typename std::enable_if<!(Kern::dim == 2), bool>::type inline void apply_kernel(){...}.






share|improve this answer





















  • @LeoStar: Thank you for your answer! So the point is simply to provide the compile with some expression that will fail, except if we are in the desired case? Is that right? In particular, there is no deeper meaning to the use of the particular construct employed in the code that I'm looking at?
    – Sam
    Dec 7 at 12:10






  • 1




    @Sam Correct. The type and default value chosen there are irrelevant, they remain unused. The entire purpose is to provide no type if the condition is not fulfilled, which leads to substitution failure (and thus ignoring that template). The default value is for convenience of the user (if you remove the default value everything still works, the user would just have to add a meaningless value to the template parameter list when calling apply_kern).
    – Max Langhof
    Dec 7 at 12:15












  • @Sam Actually you need only to write the expression that you want your compiler to compile. In the other cases in compile time the bool expression inside the std::enable_if is gonna return false and this way the function in not gonna get compiled. I would think it like the name of the enable_if: Write all the situations in which you want to get the compiler compile this. It's just a matter or implementation, but with the right bool expressions inside enable_if. If you add an extra check inside the std::enable_if like f.e. std::is_same, it gains a deeper meaning fir template metaprogramming
    – LeoSar
    Dec 7 at 12:22















up vote
1
down vote













std::enable_if<bool expression, return type of function>::type tells the compiler at compile time, compile this if the bool expression is true. So when you call the apply_kern<Kern2>() in your main() the compiler enters the first std::enable_if because you Kern::dim is indeed 2. If for example you did not call apply_kern<Kern3>(), the compiler would notice that the second std::enable_if was false and the scope inside it was not gonna get compiled. It's like an if statement, but in compile time. You could also use a templated function with 2 template aliases for Kern2 and Kern3 respectively and have the same exact result if you find this syntax strange.
For the same result in the last question i'd try typename std::enable_if<!(Kern::dim == 2), bool>::type inline void apply_kernel(){...}.






share|improve this answer





















  • @LeoStar: Thank you for your answer! So the point is simply to provide the compile with some expression that will fail, except if we are in the desired case? Is that right? In particular, there is no deeper meaning to the use of the particular construct employed in the code that I'm looking at?
    – Sam
    Dec 7 at 12:10






  • 1




    @Sam Correct. The type and default value chosen there are irrelevant, they remain unused. The entire purpose is to provide no type if the condition is not fulfilled, which leads to substitution failure (and thus ignoring that template). The default value is for convenience of the user (if you remove the default value everything still works, the user would just have to add a meaningless value to the template parameter list when calling apply_kern).
    – Max Langhof
    Dec 7 at 12:15












  • @Sam Actually you need only to write the expression that you want your compiler to compile. In the other cases in compile time the bool expression inside the std::enable_if is gonna return false and this way the function in not gonna get compiled. I would think it like the name of the enable_if: Write all the situations in which you want to get the compiler compile this. It's just a matter or implementation, but with the right bool expressions inside enable_if. If you add an extra check inside the std::enable_if like f.e. std::is_same, it gains a deeper meaning fir template metaprogramming
    – LeoSar
    Dec 7 at 12:22













up vote
1
down vote










up vote
1
down vote









std::enable_if<bool expression, return type of function>::type tells the compiler at compile time, compile this if the bool expression is true. So when you call the apply_kern<Kern2>() in your main() the compiler enters the first std::enable_if because you Kern::dim is indeed 2. If for example you did not call apply_kern<Kern3>(), the compiler would notice that the second std::enable_if was false and the scope inside it was not gonna get compiled. It's like an if statement, but in compile time. You could also use a templated function with 2 template aliases for Kern2 and Kern3 respectively and have the same exact result if you find this syntax strange.
For the same result in the last question i'd try typename std::enable_if<!(Kern::dim == 2), bool>::type inline void apply_kernel(){...}.






share|improve this answer












std::enable_if<bool expression, return type of function>::type tells the compiler at compile time, compile this if the bool expression is true. So when you call the apply_kern<Kern2>() in your main() the compiler enters the first std::enable_if because you Kern::dim is indeed 2. If for example you did not call apply_kern<Kern3>(), the compiler would notice that the second std::enable_if was false and the scope inside it was not gonna get compiled. It's like an if statement, but in compile time. You could also use a templated function with 2 template aliases for Kern2 and Kern3 respectively and have the same exact result if you find this syntax strange.
For the same result in the last question i'd try typename std::enable_if<!(Kern::dim == 2), bool>::type inline void apply_kernel(){...}.







share|improve this answer












share|improve this answer



share|improve this answer










answered Dec 7 at 11:58









LeoSar

112




112












  • @LeoStar: Thank you for your answer! So the point is simply to provide the compile with some expression that will fail, except if we are in the desired case? Is that right? In particular, there is no deeper meaning to the use of the particular construct employed in the code that I'm looking at?
    – Sam
    Dec 7 at 12:10






  • 1




    @Sam Correct. The type and default value chosen there are irrelevant, they remain unused. The entire purpose is to provide no type if the condition is not fulfilled, which leads to substitution failure (and thus ignoring that template). The default value is for convenience of the user (if you remove the default value everything still works, the user would just have to add a meaningless value to the template parameter list when calling apply_kern).
    – Max Langhof
    Dec 7 at 12:15












  • @Sam Actually you need only to write the expression that you want your compiler to compile. In the other cases in compile time the bool expression inside the std::enable_if is gonna return false and this way the function in not gonna get compiled. I would think it like the name of the enable_if: Write all the situations in which you want to get the compiler compile this. It's just a matter or implementation, but with the right bool expressions inside enable_if. If you add an extra check inside the std::enable_if like f.e. std::is_same, it gains a deeper meaning fir template metaprogramming
    – LeoSar
    Dec 7 at 12:22


















  • @LeoStar: Thank you for your answer! So the point is simply to provide the compile with some expression that will fail, except if we are in the desired case? Is that right? In particular, there is no deeper meaning to the use of the particular construct employed in the code that I'm looking at?
    – Sam
    Dec 7 at 12:10






  • 1




    @Sam Correct. The type and default value chosen there are irrelevant, they remain unused. The entire purpose is to provide no type if the condition is not fulfilled, which leads to substitution failure (and thus ignoring that template). The default value is for convenience of the user (if you remove the default value everything still works, the user would just have to add a meaningless value to the template parameter list when calling apply_kern).
    – Max Langhof
    Dec 7 at 12:15












  • @Sam Actually you need only to write the expression that you want your compiler to compile. In the other cases in compile time the bool expression inside the std::enable_if is gonna return false and this way the function in not gonna get compiled. I would think it like the name of the enable_if: Write all the situations in which you want to get the compiler compile this. It's just a matter or implementation, but with the right bool expressions inside enable_if. If you add an extra check inside the std::enable_if like f.e. std::is_same, it gains a deeper meaning fir template metaprogramming
    – LeoSar
    Dec 7 at 12:22
















@LeoStar: Thank you for your answer! So the point is simply to provide the compile with some expression that will fail, except if we are in the desired case? Is that right? In particular, there is no deeper meaning to the use of the particular construct employed in the code that I'm looking at?
– Sam
Dec 7 at 12:10




@LeoStar: Thank you for your answer! So the point is simply to provide the compile with some expression that will fail, except if we are in the desired case? Is that right? In particular, there is no deeper meaning to the use of the particular construct employed in the code that I'm looking at?
– Sam
Dec 7 at 12:10




1




1




@Sam Correct. The type and default value chosen there are irrelevant, they remain unused. The entire purpose is to provide no type if the condition is not fulfilled, which leads to substitution failure (and thus ignoring that template). The default value is for convenience of the user (if you remove the default value everything still works, the user would just have to add a meaningless value to the template parameter list when calling apply_kern).
– Max Langhof
Dec 7 at 12:15






@Sam Correct. The type and default value chosen there are irrelevant, they remain unused. The entire purpose is to provide no type if the condition is not fulfilled, which leads to substitution failure (and thus ignoring that template). The default value is for convenience of the user (if you remove the default value everything still works, the user would just have to add a meaningless value to the template parameter list when calling apply_kern).
– Max Langhof
Dec 7 at 12:15














@Sam Actually you need only to write the expression that you want your compiler to compile. In the other cases in compile time the bool expression inside the std::enable_if is gonna return false and this way the function in not gonna get compiled. I would think it like the name of the enable_if: Write all the situations in which you want to get the compiler compile this. It's just a matter or implementation, but with the right bool expressions inside enable_if. If you add an extra check inside the std::enable_if like f.e. std::is_same, it gains a deeper meaning fir template metaprogramming
– LeoSar
Dec 7 at 12:22




@Sam Actually you need only to write the expression that you want your compiler to compile. In the other cases in compile time the bool expression inside the std::enable_if is gonna return false and this way the function in not gonna get compiled. I would think it like the name of the enable_if: Write all the situations in which you want to get the compiler compile this. It's just a matter or implementation, but with the right bool expressions inside enable_if. If you add an extra check inside the std::enable_if like f.e. std::is_same, it gains a deeper meaning fir template metaprogramming
– LeoSar
Dec 7 at 12:22


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53668647%2fsfinae-what-is-happening-here%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

flock() on closed filehandle LOCK_FILE at /usr/bin/apt-mirror

Mangá

Eduardo VII do Reino Unido