Overload and this pointer

The question is more likely theoretical.

Foreword Visitor Template:

class Visitor { public: virtual void VisitElementA(const ElementA& obj) = 0; virtual void VisitElementB(const ElementB& obj) = 0; }; class Element { public: virtual void Accept(Visitor& visitor) = 0; }; class ElementA : public Element { public: void Accept(Visitor& visitor) override { visitor.VisitElementA(*this); } }; class ElementB : public Element { public: void Accept(Visitor& visitor) override { visitor.VisitElementB(*this); } }; 

This VisitElementA element (const ElementA & obj) looks a little ugly, so using overload we can rewrite it like this:

 class Visitor { public: virtual void Visit(const ElementA& obj) = 0; virtual void Visit(const ElementB& obj) = 0; }; 

And now we have two identical implementations of the Accept method in ElementA and ElementB:

 void Accept(Visitor& visitor) override { visitor.Visit(*this); } 

And such code should be added to ElementC, ElementD, etc. (if there)

Question : How to avoid this duplication?

The naive decision to place the Accept implementation inside the Element class (or some other intermediate class) will not work, because this pointer will point to the object as an object of the Element class, not ElementA or ElementB, and thus, in the best case, we get an error compilation or even incorrect behavior (if there is some overloaded Visit method for Element).

As I understand it, the problem is trying to combine compilation and runtime functions. But can there be some kind of template-based solution or a new C ++ 11 function or something else?

One note: I would appreciate it if you did not offer a solution with "macro magic" :).

+4
source share
2 answers

You can use the CRTP template.

Convert the Element class to a template class that accepts a derived type as a type parameter. You can then reset the derived type before calling the visitor:

 template <typename Derived> class Element { public: void Accept(Visitor& visitor) { visitor.Visit(*static_cast<Derived*>(this)); } }; 

Finally, each specific element is obtained from Element as follows:

 class ElementA : public Element<ElementA> { }; 

Note that Accept(Visitor&) should no longer be virtual.

Update: Here is the solution to the problem indicated by quetzalcoatl:

 class ElementC : public Element<ElementC>, public ElementA { public: using Element<ElementC>::Accept; }; 

Through the declaration, using ElementC brings the name Accept to its scope and, as a result, hidden in the base classes. However, this Accept is equal to Element<ElementC>::Accept , and in practice, only ElementA::Accept hidden.

+2
source
 class Visitor { public: virtual void Visit(const Element& obj) = 0; } class Element { public: void Accept(Visitor& visitor) { visitor.Visit(*this); } }; 

Edit2: you need to call the Visit method from the Element base class. Since Elements are polymorphic, you passed the correct object anyway. However, you may need to throw if you need to access methods that are unique to an element or introduce other abstract methods.

0
source

All Articles