Search code examples
c++language-lawyerelaborated-type-specifier

Difference between forward declaration in argument vs "normal" forward declaration


What is - if any - the difference between a forward declaration in a (template) argument (using an elaborated type specifier) and a "normal" forward declaration?

void foo(struct bar *);

// vs

struct bar;
void foo(bar *);

// ------ and also -------

std::unique_ptr<class Baz> one;

// vs

class Baz;
std::unique_ptr<Baz> two;

Solution

  • Let's begin by noting that "forward declaration" is a colloquialism used to refer to a certain common practical use of certain kinds of declarations. There is no such thing as a forward declaration as far as the C++ standard is concerned. There are just declarations.

    With that in mind, I believe that there is no difference between

    void foo(struct bar *);
    

    and

    struct bar;
    

    as far as their effect on the name bar is concerned. Both declarations end up introducing the name of the struct bar if there is no previous declaration that already did so.

    The relevant paragraph in C++17 would seem to be [basic.lookup.elab]/2 (emphasis mine):

    If the elaborated-type-specifier is introduced by the class-key and this lookup does not find a previously declared type-name, or […] the elaborated-type-specifier is a declaration that introduces the class-name as described in [basic.scope.pdecl].

    If an elaborated-type-specifier that doesn't contain a nested-name-specifier is encountered, unqualified name lookup is performed to see if the name already names a corresponding type. If no previously declared name is found, then the elaborated-type-specifier becomes a declaration of the class type of that name…

    As pointed out by geza, the one way in which there can be a difference has to do with the scope into which the name is introduced. While

    struct bar;
    

    always introduces the name into the scope in which the declaration appears, an elaborated-type-specifier appearing as part of any other kind of declaration will introduce the name into the closest enclosing namespace [basic.scope.pdecl]/7.