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
]