如果您有一个封闭范围,则情况会有所不同。
情况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
本身被称为
精细型说明符。当您使用它来声明某个东西时,编译器首先查找是否存在名为
B
的类型在作用域内(标准§3.4.4 [basic.lookup.elab]/p2,§9.1 [class.name]/p3 记号)。如果有,则认为
精细型说明符指的是那个类型。
如果没有,它将还声明一个新名称。与前向声明的区别在于此新名称的声明范围。如果用于指定命名空间范围内函数的返回类型或参数类型,则将其视为在包含声明的命名空间中声明该名称;否则,在除友元声明外的最小命名空间或块范围内声明该名称(标准§3.3.2 [basic.scope.pdecl] /p7)。请注意,这永远不会在类作用域中声明名称;换句话说,它永远不会声明嵌套类。
为什么要使用前向声明而不是其他方法?首先,它可以在任何范围内声明名称,而不仅仅是块和命名空间范围。其次,前向声明更加健壮。请注意,如果我将
test::B
的定义移到
test::A
之前,那么
test::A
中的
class B
将突然引用
test::B
而不是
::B
。第三,您真的想记住一个关于声明名称所在作用域的整个段落吗?