質問

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.

役に立ちましたか?

解決

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

他のヒント

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top