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