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.