我正在寻找当我被允许在另一个类的头文件中做类的前向声明的定义:
我是否可以为基类,为作为成员持有的类,为通过引用传递给成员函数的类,等等这样做?
我正在寻找当我被允许在另一个类的头文件中做类的前向声明的定义:
我是否可以为基类,为作为成员持有的类,为通过引用传递给成员函数的类,等等这样做?
当前回答
到目前为止,没有一个答案描述了何时可以使用类模板的前向声明。所以,开始吧。
类模板可以被声明为:
template <typename> struct X;
按照公认答案的结构,
以下是你能做的和不能做的。
使用不完整类型可以做什么:
Declare a member to be a pointer or a reference to the incomplete type in another class template: template <typename T> class Foo { X<T>* ptr; X<T>& ref; }; Declare a member to be a pointer or a reference to one of its incomplete instantiations: class Foo { X<int>* ptr; X<int>& ref; }; Declare function templates or member function templates which accept/return incomplete types: template <typename T> void f1(X<T>); template <typename T> X<T> f2(); Declare functions or member functions which accept/return one of its incomplete instantiations: void f1(X<int>); X<int> f2(); Define function templates or member function templates which accept/return pointers/references to the incomplete type (but without using its members): template <typename T> void f3(X<T>*, X<T>&) {} template <typename T> X<T>& f4(X<T>& in) { return in; } template <typename T> X<T>* f5(X<T>* in) { return in; } Define functions or methods which accept/return pointers/references to one of its incomplete instantiations (but without using its members): void f3(X<int>*, X<int>&) {} X<int>& f4(X<int>& in) { return in; } X<int>* f5(X<int>* in) { return in; } Use it as a base class of another template class template <typename T> class Foo : X<T> {} // OK as long as X is defined before // Foo is instantiated. Foo<int> a1; // Compiler error. template <typename T> struct X {}; Foo<int> a2; // OK since X is now defined. Use it to declare a member of another class template: template <typename T> class Foo { X<T> m; // OK as long as X is defined before // Foo is instantiated. }; Foo<int> a1; // Compiler error. template <typename T> struct X {}; Foo<int> a2; // OK since X is now defined. Define function templates or methods using this type template <typename T> void f1(X<T> x) {} // OK if X is defined before calling f1 template <typename T> X<T> f2(){return X<T>(); } // OK if X is defined before calling f2 void test1() { f1(X<int>()); // Compiler error f2<int>(); // Compiler error } template <typename T> struct X {}; void test2() { f1(X<int>()); // OK since X is defined now f2<int>(); // OK since X is defined now }
不完整类型不能做的事情:
Use one of its instantiations as a base class class Foo : X<int> {} // compiler error! Use one of its instantiations to declare a member: class Foo { X<int> m; // compiler error! }; Define functions or methods using one of its instantiations void f1(X<int> x) {} // compiler error! X<int> f2() {return X<int>(); } // compiler error! Use the methods or fields of one of its instantiations, in fact trying to dereference a variable with incomplete type class Foo { X<int>* m; void method() { m->someMethod(); // compiler error! int i = m->someField; // compiler error! } }; Create explicit instantiations of the class template template struct X<int>;
其他回答
Lakos区分类的使用
In-name-only(向前声明就足够了)和 In-size(为此需要类定义)。
我从来没见过比这更简洁的发音:)
只要不需要定义(比如指针和引用),就可以使用前向声明。这就是为什么大多数情况下你会在头文件中看到它们,而实现文件通常会为适当的定义拉出头文件。
我只是想补充一件重要的事情,你可以用Luc Touraille的回答中没有提到的转发类来做。
使用不完整类型可以做什么:
定义接受/返回的函数或方法 指向不完整类型的指针/引用并转发该指针/引用 到另一个函数。
void f6(X*) {}
void f7(X&) {}
void f8(X* x_ptr, X& x_ref) { f6(x_ptr); f7(x_ref); }
模块可以将前向声明类的对象传递给另一个模块。
除了指向不完整类型的指针和引用外,还可以声明函数原型来指定不完整类型的参数和/或返回值。但是,不能定义具有不完整形参或返回类型的函数,除非它是指针或引用。
例子:
struct X; // Forward declaration of X
void f1(X* px) {} // Legal: can always use a pointer
void f2(X& x) {} // Legal: can always use a reference
X f3(int); // Legal: return value in function prototype
void f4(X); // Legal: parameter in function prototype
void f5(X) {} // ILLEGAL: *definitions* require complete types
把你自己放在编译器的位置:当你转发声明一个类型时,编译器只知道这个类型存在;它对自己的规模、成员或方法一无所知。这就是为什么它被称为不完全类型。因此,不能使用类型来声明成员或基类,因为编译器需要知道类型的布局。
假设有以下正向声明。
class X;
以下是你能做的和不能做的。
使用不完整类型可以做什么:
将成员声明为指向不完整类型的指针或引用: 类Foo { X * p; X设计验证; }; 声明接受/返回不完整类型的函数或方法: 无效的f1 (X); X f2 (); 定义接受/返回指向不完整类型(但不使用其成员)的指针/引用的函数或方法: void f3(X*, X&) {} X& f4() {} X* f5() {}
不完整类型不能做的事情:
Use it as a base class class Foo : X {} // compiler error! Use it to declare a member: class Foo { X m; // compiler error! }; Define functions or methods using this type void f1(X x) {} // compiler error! X f2() {} // compiler error! Use its methods or fields, in fact trying to dereference a variable with incomplete type class Foo { X *m; void method() { m->someMethod(); // compiler error! int i = m->someField; // compiler error! } };
对于模板,没有绝对的规则:是否可以使用不完整类型作为模板形参取决于模板中使用该类型的方式。
例如,std::vector<T>要求其参数为完整类型,而boost::container::vector<T>则不要求。有时,只有在使用某些成员函数时才需要完整类型;例如,std::unique_ptr<T>就是这种情况。
一个文档完备的模板应该在其文档中指出其参数的所有需求,包括它们是否需要为完整类型。