【问题标题】:Forward defining class in namespace?在命名空间中转发定义类?
【发布时间】:2011-11-09 19:11:01
【问题描述】:

以下 sn-p 无法用 Visual Studio 2010 编译,但 GCC 喜欢它:

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 ()
{
}

我是在做一些愚蠢的事情还是这是一个有效的编译器错误?我得到的错误是: error C2888: 'void Bar::Foo(void)' : symbol cannot be defined within namespace 'Test'

它使用 GCC 4.5.1 编译:http://ideone.com/7sImY

[编辑] 为了清楚起见,我想知道这是否是有效的 C++(如果是,为什么不)——编译它的解决方法很好,但不是这个问题的一部分。

【问题讨论】:

    标签: c++ visual-c++ compiler-errors compiler-bug


    【解决方案1】:

    好吧,我也在codepad.org 中尝试过它并且它可以编译,但我不确定它是否应该(不那么精通C++ 编译器功能)!

    解决方法: 前向声明 Bar 或者您必须在创建 Foo 之前定义 Bar。换句话说,这在 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;
    }
    

    更新: 我认为这可能已经是向 Microsoft 报告的错误......这看起来非常接近:http://connect.microsoft.com/VisualStudio/feedback/details/99218/invalid-error-c2888-when-a-class-is-defined-after-it-is-declared

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

    【讨论】:

    • class Bar 应该是前向声明。我认为这就是问题的全部意义所在。
    • 是的,Kerrek 是对的。我知道如果我更早地转发声明它会起作用,但这也应该起作用。例如,如果 Bar 没有模板方法,它将起作用...
    • Anteru,看起来这是 MSVC 中的一个错误,微软不会修复它:connect.microsoft.com/VisualStudio/feedback/details/99218/…
    【解决方案2】:

    我认为代码格式正确。但要证明这一点,需要确保标准中没有任何与用法相矛盾的内容。

    来自 C++11 标准的一些相关引用:

    3.3.2 p6:

    elaborated-type-specifier中首先声明的类的声明点如下:

    • 对于类键标识符形式的详细类型说明符
      • 如果在 decl-specifier-seqparameter-declaration-clause 中使用了 elaborated-type-specifier 在命名空间范围内定义的函数,identifier 被声明为命名空间中的 class-name 包含声明;否则,除了作为友元声明外,标识符在 包含声明的最小的非类、非函数原型作用域。

    3.4.4 p2:

    如果 elaborated-type-specifier 没有 nested-name-specifier,并且除非详尽的类型说明符出现在具有以下形式的声明中: 类键属性说明符序列选择标识符 ; 标识符 根据 3.4.1 进行查找,但忽略任何已声明的非类型名称。 ... 如果 elaborated-type-specifierclass-key 并且此查找未找到先前声明的 type-name,或者如果 elaborated-type-specifier 以如下形式出现在声明中: 类键属性说明符序列选择标识符 ; elaborated-type-specifier 是一个 declaration,它引入了 3.3.2 中描述的 class-name

    7.1.6 有一些语法定义,确定 elaborated-type-specifier 在语法上可以是 type-specifier。 7.1 规定 type-specifier 在语法上可以是 decl-specifier,它是在函数 parameter-declaration中用作类型的语法单元> (8.3.5)。

    【讨论】:

      【解决方案3】:

      可能是编译器错误。

      改变参数的顺序会改变编译结果。

      namespace Test { 
      void Foo (class Bar& b, class Baz& c) - will compile.
      }
      

      【讨论】:

        【解决方案4】:

        class Bar 构造错误。你是一个不使用typedef struct { /* members */ } Foo的C程序员吗?

        任何人,您都需要在测试中定义 Bar 和 Baz:

        namespace Test {
            class Bar;
            class Baz;
        };
        

        并在声明函数参数时删除classstructunionenum关键字。

        通过该修改,它可以在 g++4.6 中干净地编译。

        【讨论】:

        • OP 在问题中说 GCC 编译它,MSVC 没有。
        • 有时允许使用class Bar 作为类型。避免这种情况通常是个好主意,因为“有时”很复杂。
        • 它使用 GCC现在编译(GCC 4.5.1 和 GCC4.6.1),所以这没有帮助。
        • @aschepler 引用 ISO 14882 中定义此构造的部分。看起来参数中的 fwd-declarations 应该是不可编译的。
        • Anywho,在msdn.microsoft.com/en-us/library/27zksbks.aspx 中,看起来 MSVC 对范围很挑剔,而函数参数范围是禁忌 - 它必须在命名空间(以及扩展名、类)范围内。
        猜你喜欢
        • 2013-09-20
        • 2011-03-17
        • 2010-10-12
        • 1970-01-01
        • 2013-05-17
        • 1970-01-01
        • 1970-01-01
        • 2020-12-03
        • 2021-12-29
        相关资源
        最近更新 更多