I'm trying to understand why this code does not compile:
// test.h
struct Base
{
virtual ~Base{};
virtual void execute() {}
virtual void execute(int) {}
virtual void execute(double) {}
}
template<class T>
struct Test : Base
{
void execute(typename std::enable_if<std::is_void<T>::value, void>::type)
{
// Do A
}
void execute(typename std::enable_if<!std::is_void<T>::value, int>::type t)
{
// Do B
}
};
// main.cpp
Test<void> t;
I get a compiler error: "no type named type".
Same error even if I modify the A version of the code with
std::enable_if<std::is_void<T>::value>
The goal is to create a class that depending on the parameter T creates a different function members. In this case 2, but I'd be interested also in more.
[Edit] I've added the inheritance part I was talking about in the comments.
When you instantiated Test<void>
, you also instantiated the declarations of all of it's member functions. That's just basic instantiation. What declarations does that give you? Something like this:
void execute(void);
void execute(<ill-formed> t);
If you were expecting SFINAE to silently remove the ill-formed overload, you need to remember that the S stands for "substitution". The substitution of template arguments into the parameters of a (member) function template. Neither execute
is a member function template. They are both regular member functions of a template specialization.
You can fix it in a couple of ways. One way would be to make those two templates, do SFINAE properly, and let overload resolution take you from there. @YSC already shows you how.
Another way is to use a helper template. This way you get your original goal, for a single member function to exist at any one time.
template<typename T>
struct TestBase {
void execute(T t) { }
};
template<>
struct TestBase<void> {
void execute() { }
};
template<class T>
struct Test : private TestBase<T> {
using TestBase<T>::execute;
};
You can choose whichever works best for your needs.
To address your edit. I think the second approach actually fits your needs better.
template<typename T>
struct TestBase : Base {
void execute(T t) override { }
};
template<>
struct TestBase<void> : Base {
void execute() override { }
};
TestBase
is the middle man that accomplishes what you seem to be after.