什么情况下使用前置声明?
我们这个时候可以先站在编译器的角度来思考这个问题:当你前置声明一个类型时,编译器就会知道这个类型的存在,但没法知道它的布局、大小、成员…所以我们也把前置声明的类型叫做不完全类型(incomplete type)。
假如你有下面的前置声明,
class X;
那么你可以做的如下,
-
定义一个指针或引用
class Foo { X *p; X &r;};
-
函数参数或返回值,但没法使用它们的成员变量或函数
void f1(X);X f2();void f3(X*, X&)X& f4()X* f5()
你不能做的如下,
-
用作基类
class Foo : X {} // compiler error!
-
定义变量
class Foo { X m; // compiler error!};void f1(X x) {} // compiler error!X f2() {} // compiler error,注意这里有个 {},和上面的是不同的
-
无法访问成员变量和成员函数
class Foo { X *m; void method() { m->someMethod(); // compiler error! int i = m->someField; // compiler error! }};
那么模板如何前置声明呢?例如以下的类模板,
template<typename Type, typename IDType=typename Type::IDType>class Mappings{public: ... Type valueFor(const IDType& id) { // return value } ...};
前置声明就是,
template<typename Type, typename IDType=typename Type::IDType> // 注意它的默认参数class Mappings;
C++ 标准容器,例如 std::vector
std::list
的前置声明需要加上它的命名空间,例如,
namespace std { template<class T, class Allocator = std::allocator<T>> class list;}