Home > Net >  struct fwd-declared in member-function parameter list not nested in enclosing struct
struct fwd-declared in member-function parameter list not nested in enclosing struct

Time:05-25

I was trying to forward-declare nested type struct A::impl and run into this issue.

The following code snippet compiles fine:

struct A {
    struct impl; // `struct impl` fwd declaration
    void f(impl); // A::f declaration
};

struct A::impl {}; // definition
void A::f(impl) {}

However, if I move the forward-declaration into the parameter list of member-function A::f, I get a compiler error:

struct A {
    void f(struct impl); // A::f declaration and `struct impl` fwd declaration
};

struct A::impl {}; // compile-time error
//struct impl {}; // < This compiles, instead
void A::f(impl) {}

It looks like I'm forward-declaring struct impl instead of struct A::impl.

The compiler error is (on Clang 14.0.0):

error: no struct named 'impl' in 'A'
struct A::impl {};

Question: why is

struct A {
    void f(struct impl);
};

different from

struct A {
    struct impl;
    void f(impl);
};

with respect to the scope of struct impl?

CodePudding user response:

why is struct A { void f(struct impl);}; different from struct A { struct impl; void f(impl); };?

The problem is that writing:

void f(struct impl); //does not forward declares impl

does not declares struct impl as a member struct of class A. Instead it just says that f is a member function that has a parameter of class-type impl. Thus, A has no nested class member named impl and hence you cannot provide an implementation for A::impl outside the class A.

//--------vvvv------>error because there is no member class named impl in struct A 
struct A::impl {};

To solve this, you must forward declare impl as you were originally doing.

struct A {
    struct impl; // nested struct member named impl
    void f(impl); // now impl can be used as a parameter because compiler know that impl is a nested struct
};

struct A::impl {}; // definition
void A::f(impl) {}

CodePudding user response:

Yes, there's a difference in what the forward declaration means here.

C Standard [basic.scope.pdecl]/7 reads:

The point of declaration of a class first declared in an elaborated-type-specifier is as follows:

  • for a declaration of the form

       class-key attribute-specifier-seqopt identifier;

the identifier is declared to be a class-name in the scope that contains the declaration, otherwise

  • for an elaborated-type-specifier of the form

       class-key identifier

if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest namespace or block scope that contains the declaration.

The original struct impl; within the definition of class A matches the first case, so it does forward declare impl in the immediate class scope, so as a member type.

In void f(struct impl); the struct impl is not a declaration all on its own with a semicolon, so the first case doesn't match. It's within a parameter-declaration-clause, but the function declaration is neither a definition nor at namespace scope. So we're left with the third case: the smallest enclosing namespace or block scope. It's not in any block scope at all, and the only namespace here is the global namespace, so it forward declares struct ::impl.

Because these details are tricky, I recommend always putting the forward declaration all on its own in the class/struct/union name ; form, not inside any other syntax.

  •  Tags:  
  • c
  • Related