SFINAE: What is happening here?
up vote
13
down vote
favorite
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++
|
show 12 more comments
up vote
13
down vote
favorite
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++
1
With the mentioned modification, your code shouldn't compile.
– geza
Dec 7 at 11:44
Bothg++
andclang++
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 insidestd::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
|
show 12 more comments
up vote
13
down vote
favorite
up vote
13
down vote
favorite
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++
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++
c++
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
Bothg++
andclang++
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 insidestd::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
|
show 12 more comments
1
With the mentioned modification, your code shouldn't compile.
– geza
Dec 7 at 11:44
Bothg++
andclang++
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 insidestd::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
|
show 12 more comments
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;
}
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 writeapply_kern<Kern2, true>();
(orfalse
, it does not matter) but onlyapply_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
|
show 3 more comments
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(){...}
.
@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 callingapply_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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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;
}
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 writeapply_kern<Kern2, true>();
(orfalse
, it does not matter) but onlyapply_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
|
show 3 more comments
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;
}
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 writeapply_kern<Kern2, true>();
(orfalse
, it does not matter) but onlyapply_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
|
show 3 more comments
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;
}
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;
}
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 writeapply_kern<Kern2, true>();
(orfalse
, it does not matter) but onlyapply_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
|
show 3 more comments
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 writeapply_kern<Kern2, true>();
(orfalse
, it does not matter) but onlyapply_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
|
show 3 more comments
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(){...}
.
@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 callingapply_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
add a comment |
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(){...}
.
@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 callingapply_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
add a comment |
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(){...}
.
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(){...}
.
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 callingapply_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
add a comment |
@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 callingapply_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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
1
With the mentioned modification, your code shouldn't compile.
– geza
Dec 7 at 11:44
Both
g++
andclang++
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 insidestd::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