Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forward declare other nested struct in C++

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?

like image 240
Johannes Avatar asked Jan 07 '19 19:01

Johannes


People also ask

Can you forward declare a nested class?

You cannot forward declare a nested structure outside the container. You can only forward declare it within the container.

Can you forward declare a struct?

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).

What is forward declaration in C?

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".

Can you forward declare a typedef?

+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.


2 Answers

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() {}
like image 167
NathanOliver Avatar answered Oct 20 '22 01:10

NathanOliver


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() {}
like image 35
Julien Villemure-Fréchette Avatar answered Oct 20 '22 00:10

Julien Villemure-Fréchette