ARTICLE AD BOX
I'm trying to implement an inheritable visitor method for a class using CRTP instead of a virtual function. First we have some code like this:
#include <iostream> #include <memory> struct A { virtual ~A() = default; }; struct B : A {}; struct C : A {}; template <typename Derived> struct Base { void dispatch(A *a) { if (dynamic_cast<C *>(a)) { return static_cast<Derived*>(this)->func(dynamic_cast<C *>(a)); } if (dynamic_cast<B *>(a)) { return static_cast<Derived*>(this)->func(dynamic_cast<B *>(a)); } return static_cast<Derived*>(this)->func(a); } void func(A*) { std::cout << "Base::func(A&)\n"; } void func(B*) { std::cout << "Base::func(B&)\n"; } void func(C*) { std::cout << "Base::func(C&)\n"; } private: Base() = default; friend Derived; }; struct D1 : Base<D1> { void func(B*) { std::cout << "D1::func(B&)\n"; } void func(C*) { std::cout << "D1::func(C&)\n"; } using Base::func; }; struct D2 : Base<D2>, D1 { void func(C*) { std::cout << "D2::func(C&)\n"; } using Base<D2>::dispatch; using D1::func; }; int main() { D2 d2; std::unique_ptr<A> a = std::make_unique<A>(); std::unique_ptr<A> b = std::make_unique<B>(); std::unique_ptr<A> c = std::make_unique<C>(); d2.dispatch(a.get()); d2.dispatch(b.get()); d2.dispatch(c.get()); }and then it output this:
~/dev1/develop$ ./test2 Base::func(A&) D1::func(B&) D2::func(C&)It looks good, the derived visitor call it's base class's function just like a virtual functions. In this way, when our several visitors have the same access patterns for most types, and only a few types need to be distinguished, we can implement only the functions that we care about.
But things goes wrong when we try to recursive call visit in the visit function, it is common when the visited object has some children node. We tried to modify struct D1:
struct D1 : Base<D1> { void func(B*) { std::cout << "D1::func(B&)\n"; // c is b's children node. std::unique_ptr<A> c = std::make_unique<C>(); // usually two ways of visit children, differs if we can know c's real type here. dispatch(c.get()); func(static_cast<C*>(c.get())); } void func(C*) { std::cout << "D1::func(C&)\n"; } using Base::func; };the output becomes:
~/dev1/develop$ ./test2 Base::func(A&) D1::func(B&) D1::func(C&) D1::func(C&) D2::func(C&)so we found it calls the wrong func which is not our expected, it does not similar to virtual function now.
The reason for this error and the solution are easy to understand; simply using virtual functions will solve the problem. I also know that virtual functions aren't the main performance issue here. However, I'd still like to know if there are techniques using CRTP that can achieve the same result?
The same implement using virtual inherit is here, thank you for your help.
struct Base { void dispatch(A *a) { if (dynamic_cast<C *>(a)) { return func(dynamic_cast<C *>(a)); } else if (dynamic_cast<B *>(a)) { return func(dynamic_cast<B *>(a)); } else { return func(a); } } virtual void func(A*) { std::cout << "Base::func(A&)\n"; } virtual void func(B*) { std::cout << "Base::func(B&)\n"; } virtual void func(C*) { std::cout << "Base::func(C&)\n"; } }; struct D1 : Base { void func(B*) override { std::cout << "D1::func(B&)\n"; // assume c is b's children node. std::unique_ptr<A> c = std::make_unique<C>(); // usually two ways of visit children, differs if we can know c's real type here. dispatch(c.get()); func(static_cast<C*>(c.get())); } void func(C*) override { std::cout << "D1::func(C&)\n"; } }; struct D2 : D1 { void func(C*) override { std::cout << "D2::func(C&)\n"; } };