A fixed-function virtual function does not use most of the derived classes for (* this)

Let's say I have the following code:

struct Z; struct A { virtual void Do (Z & z) const; }; struct B : public A {}; struct Z { void use (A const & a) {} void use (B const & b) {} }; void A::Do(Z& z) const{ z.use(*this); } 

Right now, when I call B.do , the type of this is A , which makes sense since the do implementation is defined in A

Is it possible to use B.do calls to use use (B const &) without having to copy-paste the same code for do from A to B ? In my actual code, I have about 15 (and growing) classes derived from some base class, and it seems like the waste should copy-paste the identical code for do every time.

[Edit] Clarification: all do is a call to use , nothing else. do and use are accept and visit functions from the visitor template .

+4
source share
4 answers

Since you have now figured out what you want, this is a visitor template, well, sorry, but it is. This answer shows how the dual-submit visitor template works.


I thought about using CRTP well, but this may or may not work for you, depending on the circumstances.
(Note: I used the code from the linked answer, so the names do not match, but I hope you get this idea.)

 // your Z class Visitor; // superclass needed for generic handling struct Superbase{ virtual void Accept(Visitor& v) = 0; }; // your A template<class Der> class Base : public Superbase{ public: void Accept(Visitor& v){ v.Visit(static_cast<Der&>(*this)); } }; // your B class Derived1 : public Base<Derived1> { }; // new C class Derived2 : public Base<Derived1> { }; class Visitor { public: virtual void Visit(Superbase& sup){ // generic handling of any Superbase-derived type } virtual void Visit(Derived1& d1){ // handle Derived1 } virtual void Visit(Derived2& d2){ // handle Derived1 } }; int main(){ Visitor v; Derived1 d1; d1.Accept(v); } 

The only problem: now you are not able to have a common descriptor for any type A , since functions cannot be both virtual and templates .: | Strike>
Clean it up, find a solution using the Superbase base class. :) It even allows you to have a Superbase container and make full use of polymorphism. :)

+3
source

I think this code does what you want:

 #include <iostream> struct A; struct B; struct Z { void use (A const & a); void use (B const & b); }; template<typename DERIVED> struct XX { void Do(Z& z){ Do(z,THIS()); } private: const DERIVED& THIS() const { return static_cast<const DERIVED&>(*this); } void Do(Z& z, const DERIVED& t){ z.use(t); } }; struct A : public XX<A> {}; struct B : public XX<B> {}; void Z::use (A const & a) { std::cout << "use for A" << std::endl; } void Z::use (B const & b) { std::cout << "use for B" << std::endl; } int main(){ A a; B b; Z z; a.Do(z); b.Do(z); return 0; } 

The only part of the “service” or “boiler plate” code is to get a boilerplate template built on your own type.

+2
source

You need to send a call to use depending on the type this points to, so you need to add another virtual function to A and B that just calls the correct use . I believe that do performs other actions besides calling use , of course, otherwise you really need to reimplement do in each subclass. It will look like

 struct A { virtual void Do (Z & z) const { // do stuff use(z); // do more stuff } virtual void use(Z & z) const { z.use(*this); } }; struct B : public A { virtual void use(Z & z) const { z.use(*this); } }; struct Z { void use (A const & a) {} void use (B const & b) {} }; 
0
source

I think I should disappoint you and say no. This is a compromise that you must make so that you can bring the interface from your classes to the visitor. The visitor should know which of them reports this, if you do not redefine virtual Do () in the base class, the visitor will consider you as A.

Please prove that I'm wrong! (I also see that it decided to remove the redundancy)

0
source

All Articles