Joseph Garnier
Joseph Garnier

Reputation: 117

C++: Overriding virtual pure function derived from a template class

I wrote some classes based on this excellent visitor pattern described here (my implementation is a little bit different).

template<typename... Types>
class Visitable {
public:
     virtual void accept(Visitor<Types...>& visitor) = 0;
};

class MyClass : public Visitable<int, string>
{
     virtual void accept(Visitor<int, string>& visitor)
     {
          /*** my code ***/
     }
};

This code above works but I would like implement MyClass like that:

class MyClass : public Visitable<int, string>
{
      template<typename... Types>
      virtual void accept(Visitor<Types...>& visitor)
      {
              /*** my code ***/
      }
};

Obviously I changed the call to the accept method but I have this error: "cannot instantiate abstract class". Why in this second case, accept() is not overridden ? MyClass should be templated ?

Thanks.

Upvotes: 2

Views: 315

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275800

Use the CRTP:

template<class D, class...Ts>
struct Visitable_CRTP : public Visitable<Ts...> {
  virtual void accept(Visitor<Ts...>& visitor) override final {
    return static_cast<D*>(this)->accept_impl(visitor);
  }
};
class MyClass : public Visitable_CRTP<MyClass, int, string>
{
  template<typename... Types>
  void accept_impl(Visitor<Types...>& visitor) // not virtual
  {
          /*** my code ***/
  }
};

Visitor_CRTP writes the glue code that attaches virtual accept to your template accept_impl.

If you want to have more than one accept method, we can do this:

template<class D, class...Visitables>
struct PolyVisitable_CRTP {};

template<class D, class...V0, class...Vs>
struct PolyVisitable_CRTP<D, Visitable<V0...>, Vs...>
  Visitable_CRTP<D, V0...>,
  PolyVisitable_CRTP<D, Vs...>
{};

which can be used like this:

class MyClass :
  public PolyVisitable_CRTP<MyClass,
    Visitable<int,double>,
    Visitable<std::string, char, wchar_t>,
    Visitable<>
  >
{
  template<typename... Types>
  void accept_impl(Visitor<Types...>& visitor)
  {
          /*** my code ***/
  }
};

and all of the Visitable bases's accepts will be routed to accept_impl.

Code not tested or compiled, probably contains tpyos.

Upvotes: 3

Franck
Franck

Reputation: 1635

In fact you try to implement

void VisitTable<int, string>::accept(Visitor<int, string>& visitor);

with

void MyClass::accept<int, string>(Visitor<int, string>& visitor);

In C++ it is not the same method name - one has a template qualification and the other not. Moreover template and virtual are incompatible specifiers for methods and you should have an error for

class MyClass : ... {
  ...
  template<typename... Types>
  virtual void accept(Visitor<Types...>& visitor);
};

error: templates may not be ‘virtual’

The override C++11 keyword helps to avoid such surprises.

Upvotes: 0

Related Questions