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?
You cannot forward declare a nested structure outside the container. You can only forward declare it within the container.
In C++, classes and structs can be forward-declared like this: class MyClass; struct MyStruct; In C++, classes can be forward-declared if you only need to use the pointer-to-that-class type (since all object pointers are the same size, and this is what the compiler cares about).
As others stated before, a forward declaration in C/C++ is the declaration of something with the actual definition unavailable. Its a declaration telling the compiler "there is a data type ABC".
+1 in the end because while you technically can't "forward-typedef" (i.e. you can't write "typedef A;"), you can almost certainly accomplish what the OP wants to accomplish using your trick above.
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() {}
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() {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With