Is there a syntax for incomplete nested type without a forward declaration?

332 views Asked by At

The following program produces a diagnostic error.

#include <memory>
class Containing {
    // class Nested;                 // [1]: This line seems required.
    typedef std::shared_ptr<class Nested> Ptr;
    class Nested {
        Ptr & ptr ();
        void foo (const Ptr &p) {
            p->ptr() = ptr()->ptr(); // [2]: Error here without [1]
        }
    };
};
int main () {}

The produced diagnostic is:

prog.cpp:8:14: error: invalid use of incomplete type 'class Nested'
             p->ptr() = ptr()->ptr();
              ^
prog.cpp:4:35: error: forward declaration of 'class Nested'`
     typedef std::shared_ptr<class Nested> Ptr;
                                   ^

However, if I uncomment the forward declaration, the compilation succeeds. I believe the reason is that Nested is assumed to be not nested when it is used for shared_ptr<>. If that is so, is there a syntax I can use to let shared_ptr<> know that Nested is nested without the forward declaration? Something like:

class Containing {
    typedef std::shared_ptr<class Containing::Nested> Ptr;
    //...

This question uses a minimal example to illustrate the problem. The actual structure looks like:

class Containing {
    typedef std::shared_ptr<class NestedInterface> Ptr;
    class NestedObject {
        Ptr ptr_;
        //...
    };
    class NestedInterface {
        virtual NestedObject & object () = 0;
        void foo (const Ptr &p) {
            // ...
        }
        //...
    };
    class NestedType1 : NestedInterface {
        NestedObject obj_;
        NestedObject & object () { return obj_; }
        //...
    };
    class NestedType2 : NestedInterface {
        Containing &c_;
        NestedObject & object () { return c_.nested_object_; }
        //...
    };
    //...
2

There are 2 answers

4
user541686 On BEST ANSWER

No there isn't.

However, you shouldn't need to avoid the forward declarations at all. This works:

class Containing {
    class NestedInterface;
    typedef std::shared_ptr<NestedInterface> Ptr;
    class NestedObject {
        Ptr ptr_;
        //...
    };
    class NestedInterface {
        // ...
    };
};

Sometimes cross-dependencies inside the classes might make this hard to do. In that case, all you need to do is to avoid defining the referencing classes inline, and to instead declare the other classes out-of-line, like this:

class Containing {
    class NestedInterface;
    class NestedObject;
    typedef std::shared_ptr<NestedInterface> Ptr;
};
class Containing::NestedObject {
    Ptr ptr_;
    //...
};
class Containing::NestedInterface {
};

Note that, in general, in C++, nested classes are not to be used on a whim the way you might in other languages -- you can generally achieve the same effect with outer classes, and they behave much better that way. (They don't require the definition of the outer class.) Only in a few cases have they been absolutely necessary. (std::allocator<T>::rebind<U> comes to mind.)

2
donjuedo On

You should keep that declaration class Nested; in there.

Declaring the Ptr and Nested relationship is a chicken-and-egg problem, and the forward declaration is the proper way to handle that.

The compiler needs to know that the token Nested is a class, and not something else. At the point when the compiler reaches Ptr, that info is enough, and details of the class are not yet relevant. Then the compiler reaches the complete Nested declaration, and the details can be used for all future uses of the class.