Reputation: 3099
I'm trying to implement the visitor pattern inside of another class. MWE:
struct super
{
struct base
{
virtual void accept(struct visitor& v);
virtual ~base() {}
};
struct visitor
{
virtual void visit(base& b);
virtual ~visitor() {}
};
struct special : public base
{
void accept(visitor& v) override { v.visit(*this); }
};
};
int main() {}
This complains that special::accept
is actually not overriding anything. I guess this is because of struct visitor
being different to visitor
.
Swapping the position of base and visitor (and moving the forward declaration to visitor::visit
) vanishes this error (but then says that the argument in v.visit(*this)
would not match).
Is it possible to implement the visitor pattern inside another class? Why does my forward declaration not work?
Upvotes: 5
Views: 354
Reputation: 828
Declaring
struct super
{
struct base
{
virtual void accept(struct visitor& v);
virtual ~base() {}
};
};
Does not make visitor
a member of base
nor a member of super
. It actually forward declares global ::visitor
. This is why in C++, it is considered very bad style to forward declare a type inside some other declaration. Base class function accept has the signature void accept(::visitor&)
but derived class has signature void accept(super::visitor&)
. The MWE you wrote would be equivalent to the following code:
struct super
{
struct base
{
// Declares ::visitor
// Same signature as: virtual void accept(::visitor&);
virtual void accept(struct visitor& v);
virtual ~base() {}
};
// Declares super::base::visitor
struct visitor
{
virtual void visit(base& b);
virtual ~visitor() {}
};
struct special : public base
{
// Must have signature void accept(::visitor&) to override
void accept(::visitor& v) override;
};
};
struct visitor
{
virtual void visit(super::base& b);
virtual ~visitor() {}
};
inline void super::special::accept(::visitor& v) { v.visit(*this); }
int main() {}
Upvotes: 1
Reputation: 180945
When you do
virtual void accept(struct visitor& v);
you forward declare visitor
in the
smallest namespace or block scope that contains the declaration. That means visitor is scoped to the global namespace in this case.
specials`'s
void accept(visitor& v)
on the other hand is grabbing super::visitor
. Since those are different types the compiler is correct.
What you need to do is move the forward declaration of visitor
into super
's scope like
struct super
{
struct visitor;
struct base
{
virtual void accept(visitor& v);
virtual ~base() {}
};
struct visitor
{
virtual void visit(base& b);
virtual ~visitor() {}
};
struct special : public base
{
void accept(visitor& v) override { v.visit(*this); }
};
};
int main() {}
Upvotes: 5