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?










share|improve this question




















  • 1




    SFINAE works only for deduced template parameters.
    – Kerrek SB
    Nov 28 '17 at 0:54















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?










share|improve this question




















  • 1




    SFINAE works only for deduced template parameters.
    – Kerrek SB
    Nov 28 '17 at 0:54













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?










share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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














  • 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












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






share|improve this answer























  • 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




















up vote
4
down vote













Specialize the whole class:



template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};





share|improve this answer























  • 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


















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.






share|improve this answer





















  • 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











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%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






share|improve this answer























  • 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

















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






share|improve this answer























  • 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















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






share|improve this answer














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







share|improve this answer














share|improve this answer



share|improve this answer








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




















  • 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


















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














up vote
4
down vote













Specialize the whole class:



template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};





share|improve this answer























  • 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















up vote
4
down vote













Specialize the whole class:



template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};





share|improve this answer























  • 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













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





share|improve this answer














Specialize the whole class:



template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};






share|improve this answer














share|improve this answer



share|improve this answer








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


















  • 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










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.






share|improve this answer





















  • 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















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.






share|improve this answer





















  • 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













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.






share|improve this answer












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.







share|improve this answer












share|improve this answer



share|improve this answer










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


















  • 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


















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%2f47522213%2fspecializing-a-template-method-with-enable-if%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

Florida Star v. B. J. F.

Danny Elfman

Lugert, Oklahoma