Forward defining class in namespace?
-
25-02-2021 - |
Pergunta
The following snippet fails to compile with Visual Studio 2010, but GCC likes it:
namespace Test {
class Baz;
// Adding class Bar; here and removing the class below makes it work
// with VC++, but it should work like this, shouldn't it?
void Foo (Baz& b, class Bar& c);
}
namespace Test {
class Bar
{
// Making this method non-template works
template <typename T>
static void Lalala ()
{
}
};
}
int main ()
{
}
Am I doing something stupid here or is this a valid compiler bug? The error I get is:
error C2888: 'void Bar::Foo(void)' : symbol cannot be defined within namespace 'Test'
It compiles with GCC 4.5.1: http://ideone.com/7sImY
[Edit] Just to be clear, I want to know if this is valid C++ or not (and if so, why not) -- workarounds to get it compiled are nice but not part of this question.
Solução
I think the code is well-formed. But proving that for certain would require making sure there's nothing in the Standard that contradicts the usage.
Some relevant quotes from the C++11 Standard:
3.3.2 p6:
The point of declaration of a class first declared in an elaborated-type-specifier is as follows:
- 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 non-class, non-function-prototype scope that contains the declaration.
3.4.4 p2:
If the elaborated-type-specifier has no nested-name-specifier, and unless the elaborated-type-specifier appears in a declaration with the following form: class-key attribute-specifier-seq opt identifier
;
the identifier is looked up according to 3.4.1 but ignoring any non-type names that have been declared. ... If the elaborated-type-specifier is introduced by the class-key and this lookup does not find a previously declared type-name, or if the elaborated-type-specifier appears in a declaration with the form: class-key attribute-specifier-seq opt identifier;
the elaborated-type-specifier is a declaration that introduces the class-name as described in 3.3.2.
7.1.6 has some syntax definitions establishing that an elaborated-type-specifier can syntactically be a type-specifier. 7.1 establishes that a type-specifier can syntactically be a decl-specifier, which is the syntactical unit used as the type in a function parameter-declaration (8.3.5).
Outras dicas
Well, I tried it in codepad.org too and it compiles, but I'm not sure it should (not THAT proficient in C++ compiler functionality)!
Workaround: forward declare Bar
as well or you have to define Bar
before you make Foo
. In other words, this compiles in MSVC:
namespace Test
{
class Baz;
class Bar;// also forward-declare Bar
void Foo (Baz& b, class Bar& c);
}
namespace Test
{
class Bar
{
template <typename T>
static void Foo ()
{
}
};
}
int main(void)
{
return 0;
}
Update: I think that this might already be a bug reported to Microsoft... this looks pretty close: http://connect.microsoft.com/VisualStudio/feedback/details/99218/invalid-error-c2888-when-a-class-is-defined-after-it-is-declared
The workaround cited by Microsoft:
A stand-alone forward declaration consists of an elaborated type specifier followed by a semicolon.
insert the declaration
class C2888;
before the declaration of foo(C2888o, C2888).
Probably it is a compiler bug.
Changing the order of parameters will change the compilation result.
namespace Test {
void Foo (class Bar& b, class Baz& c) - will compile.
}
The class Bar
construct is erroneous. Are you, by any chance, a C programmer who didn't use typedef struct { /* members */ } Foo
?
Anywho, you need to define both Bar and Baz inside test:
namespace Test {
class Bar;
class Baz;
};
And remove class
, struct
, union
, and enum
keywords when declaring function parameters.
With that modification, it compiles cleanly in g++4.6.