enable_if in function members for void and inheritance

  • A+
Category:Languages

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.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: