Specializing a template method with enable_if
up vote
6
down vote
favorite
I'm writing a template class that stores a std::function
in order to call it later. Here is the simplifed code:
template <typename T>
struct Test
{
void call(T type)
{
function(type);
}
std::function<void(T)> function;
};
The problem is that this template does not compile for the void
type because
void call(void type)
becomes undefined.
Specializing it for the void
type doesn't alleviate the problem because
template <>
void Test<void>::call(void)
{
function();
}
is still incompatible with the declaration of call(T Type)
.
So, using the new features of C++ 11, I tried std::enable_if
:
typename std::enable_if_t<std::is_void_v<T>, void> call()
{
function();
}
typename std::enable_if_t<!std::is_void_v<T>, void> call(T type)
{
function(type);
}
but it does not compile with Visual Studio:
error C2039: 'type' : is not a member of 'std::enable_if'
How would you tackle this problem?
c++ c++11 templates sfinae template-specialization
add a comment |
up vote
6
down vote
favorite
I'm writing a template class that stores a std::function
in order to call it later. Here is the simplifed code:
template <typename T>
struct Test
{
void call(T type)
{
function(type);
}
std::function<void(T)> function;
};
The problem is that this template does not compile for the void
type because
void call(void type)
becomes undefined.
Specializing it for the void
type doesn't alleviate the problem because
template <>
void Test<void>::call(void)
{
function();
}
is still incompatible with the declaration of call(T Type)
.
So, using the new features of C++ 11, I tried std::enable_if
:
typename std::enable_if_t<std::is_void_v<T>, void> call()
{
function();
}
typename std::enable_if_t<!std::is_void_v<T>, void> call(T type)
{
function(type);
}
but it does not compile with Visual Studio:
error C2039: 'type' : is not a member of 'std::enable_if'
How would you tackle this problem?
c++ c++11 templates sfinae template-specialization
1
SFINAE works only for deduced template parameters.
– Kerrek SB
Nov 28 '17 at 0:54
add a comment |
up vote
6
down vote
favorite
up vote
6
down vote
favorite
I'm writing a template class that stores a std::function
in order to call it later. Here is the simplifed code:
template <typename T>
struct Test
{
void call(T type)
{
function(type);
}
std::function<void(T)> function;
};
The problem is that this template does not compile for the void
type because
void call(void type)
becomes undefined.
Specializing it for the void
type doesn't alleviate the problem because
template <>
void Test<void>::call(void)
{
function();
}
is still incompatible with the declaration of call(T Type)
.
So, using the new features of C++ 11, I tried std::enable_if
:
typename std::enable_if_t<std::is_void_v<T>, void> call()
{
function();
}
typename std::enable_if_t<!std::is_void_v<T>, void> call(T type)
{
function(type);
}
but it does not compile with Visual Studio:
error C2039: 'type' : is not a member of 'std::enable_if'
How would you tackle this problem?
c++ c++11 templates sfinae template-specialization
I'm writing a template class that stores a std::function
in order to call it later. Here is the simplifed code:
template <typename T>
struct Test
{
void call(T type)
{
function(type);
}
std::function<void(T)> function;
};
The problem is that this template does not compile for the void
type because
void call(void type)
becomes undefined.
Specializing it for the void
type doesn't alleviate the problem because
template <>
void Test<void>::call(void)
{
function();
}
is still incompatible with the declaration of call(T Type)
.
So, using the new features of C++ 11, I tried std::enable_if
:
typename std::enable_if_t<std::is_void_v<T>, void> call()
{
function();
}
typename std::enable_if_t<!std::is_void_v<T>, void> call(T type)
{
function(type);
}
but it does not compile with Visual Studio:
error C2039: 'type' : is not a member of 'std::enable_if'
How would you tackle this problem?
c++ c++11 templates sfinae template-specialization
c++ c++11 templates sfinae template-specialization
edited Nov 11 at 22:11
max66
34.1k63762
34.1k63762
asked Nov 28 '17 at 0:45
Mark Morrisson
5911717
5911717
1
SFINAE works only for deduced template parameters.
– Kerrek SB
Nov 28 '17 at 0:54
add a comment |
1
SFINAE works only for deduced template parameters.
– Kerrek SB
Nov 28 '17 at 0:54
1
1
SFINAE works only for deduced template parameters.
– Kerrek SB
Nov 28 '17 at 0:54
SFINAE works only for deduced template parameters.
– Kerrek SB
Nov 28 '17 at 0:54
add a comment |
3 Answers
3
active
oldest
votes
up vote
2
down vote
accepted
SFINAE doesn't work over (only) the template parameters of the class/structs.
Works over template methods whit conditions involving template parameters of the method.
So you have to write
template <typename U = T>
std::enable_if_t<std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<!std::is_void<U>::value> call(T type)
{ function(type); }
or, if you want to be sure that U
is T
template <typename U = T>
std::enable_if_t< std::is_same<U, T>::value
&& std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<std::is_same<U, T>::value
&& !std::is_void<U>::value> call(T type)
{ function(type); }
p.s.: std::enable_if_t
is a type so doesn't require typename
before.
p.s.2: you tagged C++11 but your example use std::enable_if_t
, that is C++14, and std::is_void_v
, that is C++17
I didn't fully understand the subtlety that explains why SFINAE works in your case and not mine, but your code compiles. And thank you for your additional comments.
– Mark Morrisson
Nov 28 '17 at 20:45
For production-level code, you probably also want astd::is_same_v<T, U>
inside theenable_if_t
to thwart an explicitcall<U>()
forU
unequal toT
from compiling.
– TemplateRex
Nov 29 '17 at 13:17
add a comment |
up vote
4
down vote
Specialize the whole class:
template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};
Sure it works, but the idea was to precisely avoid specializing the whole class by using recent SFINAE constructs.
– Mark Morrisson
Nov 28 '17 at 20:46
add a comment |
up vote
2
down vote
If you don't stick to use void
, and your intention is to actually able to use Test
without any parameters, then use variadic template:
template <typename ...T>
struct Test
{
void call(T ...type)
{
function(type...);
}
std::function<void(T...)> function;
};
This way, you can have any number of parameters. If you want to have no parameters, use this:
Test<> t;
t.call();
So this is not exactly the syntax you wanted, but there is no need for specialization, and this solution is more flexible, because supports any number of parameters.
I gave a simplified version of the code, but actually the idea was to handle a void return type. I handled the input parameters with a variadic template as you suggested.
– Mark Morrisson
Nov 28 '17 at 20:46
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%2f47522213%2fspecializing-a-template-method-with-enable-if%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
SFINAE doesn't work over (only) the template parameters of the class/structs.
Works over template methods whit conditions involving template parameters of the method.
So you have to write
template <typename U = T>
std::enable_if_t<std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<!std::is_void<U>::value> call(T type)
{ function(type); }
or, if you want to be sure that U
is T
template <typename U = T>
std::enable_if_t< std::is_same<U, T>::value
&& std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<std::is_same<U, T>::value
&& !std::is_void<U>::value> call(T type)
{ function(type); }
p.s.: std::enable_if_t
is a type so doesn't require typename
before.
p.s.2: you tagged C++11 but your example use std::enable_if_t
, that is C++14, and std::is_void_v
, that is C++17
I didn't fully understand the subtlety that explains why SFINAE works in your case and not mine, but your code compiles. And thank you for your additional comments.
– Mark Morrisson
Nov 28 '17 at 20:45
For production-level code, you probably also want astd::is_same_v<T, U>
inside theenable_if_t
to thwart an explicitcall<U>()
forU
unequal toT
from compiling.
– TemplateRex
Nov 29 '17 at 13:17
add a comment |
up vote
2
down vote
accepted
SFINAE doesn't work over (only) the template parameters of the class/structs.
Works over template methods whit conditions involving template parameters of the method.
So you have to write
template <typename U = T>
std::enable_if_t<std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<!std::is_void<U>::value> call(T type)
{ function(type); }
or, if you want to be sure that U
is T
template <typename U = T>
std::enable_if_t< std::is_same<U, T>::value
&& std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<std::is_same<U, T>::value
&& !std::is_void<U>::value> call(T type)
{ function(type); }
p.s.: std::enable_if_t
is a type so doesn't require typename
before.
p.s.2: you tagged C++11 but your example use std::enable_if_t
, that is C++14, and std::is_void_v
, that is C++17
I didn't fully understand the subtlety that explains why SFINAE works in your case and not mine, but your code compiles. And thank you for your additional comments.
– Mark Morrisson
Nov 28 '17 at 20:45
For production-level code, you probably also want astd::is_same_v<T, U>
inside theenable_if_t
to thwart an explicitcall<U>()
forU
unequal toT
from compiling.
– TemplateRex
Nov 29 '17 at 13:17
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
SFINAE doesn't work over (only) the template parameters of the class/structs.
Works over template methods whit conditions involving template parameters of the method.
So you have to write
template <typename U = T>
std::enable_if_t<std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<!std::is_void<U>::value> call(T type)
{ function(type); }
or, if you want to be sure that U
is T
template <typename U = T>
std::enable_if_t< std::is_same<U, T>::value
&& std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<std::is_same<U, T>::value
&& !std::is_void<U>::value> call(T type)
{ function(type); }
p.s.: std::enable_if_t
is a type so doesn't require typename
before.
p.s.2: you tagged C++11 but your example use std::enable_if_t
, that is C++14, and std::is_void_v
, that is C++17
SFINAE doesn't work over (only) the template parameters of the class/structs.
Works over template methods whit conditions involving template parameters of the method.
So you have to write
template <typename U = T>
std::enable_if_t<std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<!std::is_void<U>::value> call(T type)
{ function(type); }
or, if you want to be sure that U
is T
template <typename U = T>
std::enable_if_t< std::is_same<U, T>::value
&& std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<std::is_same<U, T>::value
&& !std::is_void<U>::value> call(T type)
{ function(type); }
p.s.: std::enable_if_t
is a type so doesn't require typename
before.
p.s.2: you tagged C++11 but your example use std::enable_if_t
, that is C++14, and std::is_void_v
, that is C++17
edited Nov 28 '17 at 2:03
answered Nov 28 '17 at 1:57
max66
34.1k63762
34.1k63762
I didn't fully understand the subtlety that explains why SFINAE works in your case and not mine, but your code compiles. And thank you for your additional comments.
– Mark Morrisson
Nov 28 '17 at 20:45
For production-level code, you probably also want astd::is_same_v<T, U>
inside theenable_if_t
to thwart an explicitcall<U>()
forU
unequal toT
from compiling.
– TemplateRex
Nov 29 '17 at 13:17
add a comment |
I didn't fully understand the subtlety that explains why SFINAE works in your case and not mine, but your code compiles. And thank you for your additional comments.
– Mark Morrisson
Nov 28 '17 at 20:45
For production-level code, you probably also want astd::is_same_v<T, U>
inside theenable_if_t
to thwart an explicitcall<U>()
forU
unequal toT
from compiling.
– TemplateRex
Nov 29 '17 at 13:17
I didn't fully understand the subtlety that explains why SFINAE works in your case and not mine, but your code compiles. And thank you for your additional comments.
– Mark Morrisson
Nov 28 '17 at 20:45
I didn't fully understand the subtlety that explains why SFINAE works in your case and not mine, but your code compiles. And thank you for your additional comments.
– Mark Morrisson
Nov 28 '17 at 20:45
For production-level code, you probably also want a
std::is_same_v<T, U>
inside the enable_if_t
to thwart an explicit call<U>()
for U
unequal to T
from compiling.– TemplateRex
Nov 29 '17 at 13:17
For production-level code, you probably also want a
std::is_same_v<T, U>
inside the enable_if_t
to thwart an explicit call<U>()
for U
unequal to T
from compiling.– TemplateRex
Nov 29 '17 at 13:17
add a comment |
up vote
4
down vote
Specialize the whole class:
template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};
Sure it works, but the idea was to precisely avoid specializing the whole class by using recent SFINAE constructs.
– Mark Morrisson
Nov 28 '17 at 20:46
add a comment |
up vote
4
down vote
Specialize the whole class:
template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};
Sure it works, but the idea was to precisely avoid specializing the whole class by using recent SFINAE constructs.
– Mark Morrisson
Nov 28 '17 at 20:46
add a comment |
up vote
4
down vote
up vote
4
down vote
Specialize the whole class:
template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};
Specialize the whole class:
template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};
edited Nov 28 '17 at 1:12
answered Nov 28 '17 at 0:54
Jarod42
112k1299179
112k1299179
Sure it works, but the idea was to precisely avoid specializing the whole class by using recent SFINAE constructs.
– Mark Morrisson
Nov 28 '17 at 20:46
add a comment |
Sure it works, but the idea was to precisely avoid specializing the whole class by using recent SFINAE constructs.
– Mark Morrisson
Nov 28 '17 at 20:46
Sure it works, but the idea was to precisely avoid specializing the whole class by using recent SFINAE constructs.
– Mark Morrisson
Nov 28 '17 at 20:46
Sure it works, but the idea was to precisely avoid specializing the whole class by using recent SFINAE constructs.
– Mark Morrisson
Nov 28 '17 at 20:46
add a comment |
up vote
2
down vote
If you don't stick to use void
, and your intention is to actually able to use Test
without any parameters, then use variadic template:
template <typename ...T>
struct Test
{
void call(T ...type)
{
function(type...);
}
std::function<void(T...)> function;
};
This way, you can have any number of parameters. If you want to have no parameters, use this:
Test<> t;
t.call();
So this is not exactly the syntax you wanted, but there is no need for specialization, and this solution is more flexible, because supports any number of parameters.
I gave a simplified version of the code, but actually the idea was to handle a void return type. I handled the input parameters with a variadic template as you suggested.
– Mark Morrisson
Nov 28 '17 at 20:46
add a comment |
up vote
2
down vote
If you don't stick to use void
, and your intention is to actually able to use Test
without any parameters, then use variadic template:
template <typename ...T>
struct Test
{
void call(T ...type)
{
function(type...);
}
std::function<void(T...)> function;
};
This way, you can have any number of parameters. If you want to have no parameters, use this:
Test<> t;
t.call();
So this is not exactly the syntax you wanted, but there is no need for specialization, and this solution is more flexible, because supports any number of parameters.
I gave a simplified version of the code, but actually the idea was to handle a void return type. I handled the input parameters with a variadic template as you suggested.
– Mark Morrisson
Nov 28 '17 at 20:46
add a comment |
up vote
2
down vote
up vote
2
down vote
If you don't stick to use void
, and your intention is to actually able to use Test
without any parameters, then use variadic template:
template <typename ...T>
struct Test
{
void call(T ...type)
{
function(type...);
}
std::function<void(T...)> function;
};
This way, you can have any number of parameters. If you want to have no parameters, use this:
Test<> t;
t.call();
So this is not exactly the syntax you wanted, but there is no need for specialization, and this solution is more flexible, because supports any number of parameters.
If you don't stick to use void
, and your intention is to actually able to use Test
without any parameters, then use variadic template:
template <typename ...T>
struct Test
{
void call(T ...type)
{
function(type...);
}
std::function<void(T...)> function;
};
This way, you can have any number of parameters. If you want to have no parameters, use this:
Test<> t;
t.call();
So this is not exactly the syntax you wanted, but there is no need for specialization, and this solution is more flexible, because supports any number of parameters.
answered Nov 28 '17 at 1:52
geza
12.5k32775
12.5k32775
I gave a simplified version of the code, but actually the idea was to handle a void return type. I handled the input parameters with a variadic template as you suggested.
– Mark Morrisson
Nov 28 '17 at 20:46
add a comment |
I gave a simplified version of the code, but actually the idea was to handle a void return type. I handled the input parameters with a variadic template as you suggested.
– Mark Morrisson
Nov 28 '17 at 20:46
I gave a simplified version of the code, but actually the idea was to handle a void return type. I handled the input parameters with a variadic template as you suggested.
– Mark Morrisson
Nov 28 '17 at 20:46
I gave a simplified version of the code, but actually the idea was to handle a void return type. I handled the input parameters with a variadic template as you suggested.
– Mark Morrisson
Nov 28 '17 at 20:46
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%2f47522213%2fspecializing-a-template-method-with-enable-if%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
SFINAE works only for deduced template parameters.
– Kerrek SB
Nov 28 '17 at 0:54