转发类声明可以与使用地点的class关键字互换?

时间:2014-06-26 05:22:06

标签: c++

这两个相同吗?

代码1:

class B;

class A {
public:
    B fun1() const;
    B* m_b;
};

extern void myfun( const B& b );

代码2:

class A {
public:
    class B fun1() const;
    class B* m_b;
};

extern void myfun( const class B& b );

或者代码2中是否存在使用编程风格的问题点?

1 个答案:

答案 0 :(得分:4)

如果你有一个封闭的范围,这些是不同的。

案例1:

class B { };
namespace test {
    class B;  // declares test::B

    class A {
    public:
        B fun1() const; // refers to test::B
        B* m_b;         // also refers to test::B
    };

    class B { int x; };  // defines test::B
}

案例2:

class B { };
namespace test {
    class A {
    public:
        class B fun1() const; // refers to ::B
        class B* m_b;         // also refers to ::B
    };

    class B { int x; };  // defines test::B
}

更多详情:

class B;是两件事之一。它可以是重新声明,也可以是将B名称引入当前范围的前向声明(标准的§9.1[class.name] / p2)。

class B本身称为 elaborated-type-specifier 。当你用它来声明某些东西时,编译器首先查找一个名为B的类型是否在范围内(§3.4.4[basic.lookup.elab] / p2,§9.1[class.name] / p3注)。如果有,则使用 elaborated-type-specifier 来引用它。

如果没有,那么它也会声明一个新名称。与前向声明的区别在于声明此新名称的范围。如果用于指定命名空间作用域函数的返回类型或参数类型,则需要在包含声明的命名空间中声明名称;否则,除了作为友元声明之外,该名称在包含声明的最小名称空间或块作用域中声明(§3.3.2[basic.scope.pdecl] / p7)。请注意,这永远不会在类范围中声明名称;换句话说,它永远不会声明一个嵌套类。

为什么使用前向声明而不是替代声明?首先,它可以在任何范围内声明名称,而不仅仅是块和命名空间范围。第二,前瞻性声明不那么脆弱。请注意,在第二种情况下,如果我在test::B之前移动test::A的定义,那么class B中的test::A突然指的是test::B而不是{{} 1}}。第三,你真的想要记住关于声明名称放在哪个范围内的整段规则吗?