0

I am studying the visitor pattern from the book C++ Design Pattern by Fedor Pikus.

I understand recursive inheritance somewhat and the use of keyword using. But i am confused regarding the behavior of below code. In the code below, i have an abstract visitable class called Pet which is visited by PetVisitor like so:

template <typename ... Types>
class Visitor;


template <typename T, typename ... Types>
class Visitor<T, Types ...> : public Visitor<Types ...> {
public:
//using Visitor<Types ...>::visit;    //**************** LINE 1.

virtual void visit(T* t) = 0;
};

template <typename T>
class Visitor<T> {
public:
virtual void visit(T* t) = 0;
};

using PetVisitor = Visitor<class Cat, class Dog>;

//****** SNIPPET 1 BEGINS
template<typename Derived>
struct Pet
{
 void accept(PetVisitor& v) 
 {
    v.visit(static_cast<Derived*>(this));
 }
};

class Cat : public Pet<Cat>
{
 // using Visitable<Cat>::Visitable;
};

class Dog : public Pet<Dog>
{
 // using Visitable<Dog>::Visitable;
};

//****** SNIPPET 1 ENDS

//****** SNIPPET 2 BEGINS
//struct Pet
//{
//    virtual void accept(PetVisitor& v)  = 0;
//};
//
//template<typename Derived>
//struct Visitable : public Pet
//{
//    using Pet::Pet;
//    void accept(PetVisitor& v) override
//    {
//        v.visit(static_cast<Derived*>(this));
//    }
//};
//class Cat :  public Visitable<Cat>
//{
//    using Visitable<Cat>::Visitable;
//};
//
//class Dog : public Visitable<Dog>
//{
//    using Visitable<Dog>::Visitable;
//};
//****** SNIPPET 2 ENDS

class FeedingVisitor : public PetVisitor {
public:
 
    void visit(Cat* c) override { std::cout << "Feed tuna to the  cat" << std::endl; }
    void visit(Dog* d) override { std::cout << "feed steak to the  dog" << std::endl;}
};

In the code above I want to focus on line 1, SNIPPET 1 and SNIPPET 2. In this state, the code compiles, and if I create a visitor and visit any of the Dog or Cat, everything works as it should.

But once I comment SNIPPET1 and uncomment SNIPPET 2 the code fails to compile with the error. no matching function for call to 'Visitor<Cat, Dog>::visit(Dog*)' v.visit(static_cast<Derived*>(this)); //Leads to template instantiation.

This error goes if I uncomment Line 1. But my question is why do I need to un-comment line 1 ? From my understanding the visitor classes would be generated like so:

class Visitor<Cat, Dog> : public Visitor<Dog>
{
   public:
   virtual void visit(Cat* t) = 0;
}

and

class Visitor<Dog>
{
  public: 
  virtual void visit(Dog* t) = 0; 
}

So we do indeed have visit fn accepting both Cat and Dog type.

Edit : I am using Microsoft (R) C/C++ Optimizing Compiler Version 19.33.31629 for x86

The error with above compiler is:

 error C2664: 'void Visitor<Cat,Dog>::visit(T *)': cannot convert 
 argument 1 from 'Derived *' to 'T *'
 with
 [
   T=Cat
 ]
 and
 [
   Derived=Dog
 ]
 and
 [
   T=Cat
 ]
warrior_monk
  • 383
  • 2
  • 14
  • 1
    Don't add "accidental" complexity to your code, all this inheritance is really not necessary, just have freestanding visit functions and overload on animal type. In fact most of the time even in OO, inheritance is NOT the solution. I think Pikus is trying to show you that variadic multiple inheritance is possible, but I still think it is a bad idea. – Pepijn Kramer Mar 03 '23 at 06:33
  • What compiler are you getting this error on? I've checked both clang and gcc and [it compiles without error](https://godbolt.org/z/ehqTrTcGf) – Patrick Roberts Mar 03 '23 at 06:40
  • 2
    Thanks, here's the repro based on the edit for anyone else curious: https://godbolt.org/z/vef45nT93 – Patrick Roberts Mar 03 '23 at 06:55
  • At first I was going to skip this question because it looks overly complex, and the error message in the question does not tell me where to focus my attention. However, the **full** error message (as seen in @PatrickRoberts godbolt link) is illuminating. This is why the **full** error message should be copied into a question, not just the part you think is important. (In this case, the part I'm interested in starts with the word "error" and ends with the explanations of what the compiler substiuted for `T`, `Derived`, and `T`.) – JaMiT Mar 03 '23 at 08:54
  • 1
    Does this answer your question? [Why does an overridden function in the derived class hide other overloads of the base class?](https://stackoverflow.com/questions/1628768/why-does-an-overridden-function-in-the-derived-class-hide-other-overloads-of-the) – JaMiT Mar 03 '23 at 08:55
  • Thanks for the link, but it does not answer my question. This is does not seem to be a problem with name hiding(correct me if i am wrong), as i see the code in snippet 1 works. – warrior_monk Mar 03 '23 at 09:03
  • 1
    *"This is does not seem to be a problem with name hiding(correct me if i am wrong)"* -- I can do that. You're wrong. The code in snippet 1 does **not** work. Sure, you didn't get an error when compiling it, but that's because you didn't instantiate the function with the problem. Try compiling snippet 1 after adding `int main() { Dog d; FeedingVisitor v; d.accept(v); }`. You've made your situation so complex that your problem has no problem hiding from your compiler. (Snippet 2 gave you an error because `accept()` is virtual, so the compiler had to instantiate it to create `Dog`.) – JaMiT Mar 04 '23 at 01:30
  • @JaMiT Sorry my bad. I read your comments and the post you pointed out and that together answers my question. Thanks for that. The `using` keyword is required to avoid the `name hiding` phenomenon. – warrior_monk Mar 04 '23 at 03:09

0 Answers0