我很好奇是否有可能创建两个类,每个类都拥有另一个类的std::vector
。我的第一个猜测是,这是不可能的,因为std::vector
需要一个完整的类型,而不仅仅是一个前向声明。
#include <vector>
class B;
class A { std::vector<B> b; };
class B { std::vector<A> a; };
我认为std::vector<B>
的声明会立即导致失败,因为此时B
的类型不完整。但是,这在gcc和clang下成功编译,没有任何警告。为什么这不会导致错误?
答案 0 :(得分:5)
T.C评论说,这实际上是未定义的行为,正在对标准的变更请求中解决。违反的规则显然是[res.on.functions] 2.5:
特别是,在以下情况下效果未定义:[...]
- 如果在实例化模板组件时将不完整类型(3.9)用作模板参数,除非特别允许该组件。
无论如何它的作用(以及它可以标准化的原因)是双重的:
您的程序只包含类型定义;没有创建对象,没有调用函数,也没有生成代码。如果我们通过消除B的定义(不需要它)来简化它,然后尝试创建A
的实例,它就会失败:
class B;
class A { std::vector<B> b; };
A a; // error: ctor and dtor undefined for incomplete type B.
正如预期的那样,失败也是一个简单的
std::vector<B> b;
两种情况下的原因是编译器必须生成代码,而不仅仅是语法相关的纯类型声明。
在其他环境中使用vector type 也是可以的:
typedef std::vector<B> BVec;
可以定义班级A
,因为正如尼古拉在答案中正确说明的那样,std::vector<B>
的大小,以及A
成员的大小b
,不依赖于B
的定义(因为向量包含指向元素数组的指针,而不是适当的数组)。
答案 1 :(得分:4)
这会编译并运行正常,因为std::vector
使用指针而不是A
或B
的确切定义。您可以修改您的示例以在类的定义中使用单个实例或数组,如此
class B;
class A { public: B b[2]; };
class B { public: A a[2]; };
这显然无法编译,因为您尝试使用类的定义。编译器的错误就像你期待的那样
错误:字段'b'的类型不完整'B [2]'
然而
class B;
class A { public: B* b; };
class B { public: A* a; };
就像std::vector
一样。因此,您不需要完全定义的类来使用指针或对此类型的引用。
还有模板的简化示例
template<typename T>
struct C {
T* t;
};
class B;
class A { public: C<B> b; };
class B { public: C<A> a; };
这也可以,当然你可以实例化它
int main() {
B b;
A a;
}
答案 2 :(得分:3)
这是因为模板实例化规则。在A
声明时,只需要std::vector<B>
的接口进行实例化。
由于std::vector
的接口仅使用指针和对其值类型的引用,并且示例中的特化实例化但未使用其任何函数,因此不会出现编译器错误。< / p>
至于为什么编译器没有生成A::A()
- 这会通过调用std::vector<B>
的构造函数来触发错误 - 因为你的上下文只需要它的声明。如果编译器看到它们被使用,则只需要为这些特殊成员函数生成默认值。
§14.7.1
[...]类模板特化的隐式实例化导致类成员函数,成员类,作用域成员枚举,静态数据成员和类成员函数,成员类,定义成员枚举的定义或默认参数的隐式实例化。成员模板[。]
§12
[1] [...]当程序没有显式声明它们时,实现将隐式声明某些类类型的这些[特殊]成员函数。 如果使用它们,实现将隐式定义它们。