c++inheritancelanguage-lawyerforward-declaration

is a class declaration before base-clause a valid forward declaration?


I'm not sure if my question title is the most appropriate and I will change it happily to something clearer if I get adequate suggestion.

In a C++ video whose subject is not the one here, I saw this definition:

#include <vector>

// before ":" V is forward declared, it's enough for std::vector?
// node in a tree
struct V : std::vector<V> {};

(comments are my own).

My understanding is that it is possible to have this seemingly circular declaration, because at the base-clause location, struct V is already (forward) declared.

But is it legal C++ as I don't find that class declaration is explicit about this.

forward declaration is defined by itself (struct V;, then you can declare a std::vector<V>) but not in the full class declaration syntax.

The wording in derived class:

Any class type (whether declared with class-key class or struct) may be declared as derived...

(bold mine) seems to imply that class-key class or class-key struct (without ending ;) are already full declarations. Is it true, is it more explicit in the standard?

If yes, it may give me opportunities for interesting designs, so I would like to be sure that it's legal.

[EDIT] a more explicit example, without std::vector

template <typename T>
struct refwrapper {
    T* wrapped;
    virtual ~refwrapper() = default;
};

template <typename T>
struct plainwrapper {
    T wrapped;
    virtual ~plainwrapper() = default;
};

// OK? R is declared at the base-clause
struct R : refwrapper<R> {};

// KO P is incomplete at the base-clause
// struct P: plainwrapper<P>
// {};

Live


Solution

  • The class name is visible to lookup immediately after the identifier. See [basic.scope.pdecl]/3.

    However, as usual, until the closing } of the class definition the class is incomplete. The base class specifier is not an exception as some so-called complete class contexts are.

    Using a type as base class requires it to be complete. Therefore std::vector<V> will be instantiated by the use as base class specifier, where V is still incomplete.

    Generally instantiating a standard library template with an incomplete type as template argument causes undefined behavior.

    However, since C++17 std::vector is one of the exceptions: It is permitted to instantiate std::vector with an incomplete type as template argument as long as the type is completed before any member function of the std::vector specialization is referenced.

    So yes, struct V : std::vector<V> {}; is valid since C++17 since it doesn't reference any member of std::vector<V> before the closing }.