为什么C++容器不允许不完整类型?

20

为什么 C++ 不允许实例化不完整类型的容器?

当然可以编写没有此限制的容器 -- boost::container 完全能够做到这一点。据我所见,它似乎并没有给出任何性能或其他类型的收益,然而标准却将其定义为未定义行为。

它确实防止递归数据结构的建立,例如。

那么为什么C++标准会强制施加这种武断的限制呢?允许在可能的情况下将不完整类型作为模板参数会有什么缺点吗?


9
因为容器(除非它们存储指针)需要存储所存储对象类型的大小。 - Viktor Sehr
2
@ViktorSehr: 除了 array 之外,所有标准容器都(直接)存储指针,而不是对象;因此,在需要分配一个或多个对象之前,它们不应该需要完整的类型。 - Mike Seymour
5
因为C++11规范中提到:“17.6.4.8其他函数(...)2.在以下情况下,效果是未定义的:(...)特别地-当实例化模板组件时使用不完整的类型(3.9)作为模板参数,除非该组件明确允许。” - user541686
1
所有标准容器(除了数组)都会(直接)存储指针 — [需要引用]。 - n. m.
2
@n.m. 无法交换数组元素而不使迭代器失效。如果通过移动元素进行交换,则迭代器将不再引用相同的对象。此外,对于大多数容器,swap需要在常数时间内完成,如果必须交换每个元素,则这是不可能的。 - Mike Seymour
显示剩余12条评论
1个回答

20
马特·奥斯特恩是 C++ 标准化委员会库工作组的主席,他在他的Dr. Dobb's article中解释了委员会做出这个决定的历史原因:
我们发现,在更多的测试中,即使是简单的示例也不能与每个 STL 实现一起使用。最终,这一切似乎都太模糊、太难以理解了;标准化委员会认为没有选择,只能说 STL 容器不应该与不完整类型一起使用。为了保险起见,我们还将这个禁令应用到了标准库的其余部分。
我理解这样做是因为委员会不想通过追溯性地要求现有的库实现支持不完整类型来使这些实现无效。
在同一篇文章中,他承认:
在未来的 C++ 修订版中,放宽使用不完整类型实例化标准库模板的限制可能是有意义的。
考虑到这篇文章的日期是2002年,而且禁令在当前标准中仍然存在,我认为 Boost 设计者决定不等待未来并构建允许不完整类型的自己的容器是完全合理的。

编辑:请参阅此答案,了解在标准C++库中使用C++17标准允许的不完整类型的一些容器的信息。


1
《Dr. Dobbs》的文章已经过时了。Boost现在已经实现了它,而且显然是可能的。你最后一句话有些道理,但《Dr. Dobbs》的文章并没有... - user541686
2
@Mehrdad 标准委员会(像大多数委员会一样)在做决定时非常缓慢。有时候这是好事;有时候(我认为 C++ 标准库就是这种情况)它太过严格了。这篇文章的年代证明了这一点:至少在 std::vector<T> 上,这种限制早就应该被取消了。 - Sergey Kalinichenko
1
我真的不认为它们“慢”... 而且他们不想要求库在回溯支持这一点的想法很奇怪。毕竟,这是一个新的C++标准--有大量更复杂的变化需要添加;不添加这个的好处是什么? - user541686
1
@Mehrdad 我认为这里唯一的“好处”就是让一些现有的实现保持不变。特别是,我认为没有任何逻辑上的理由继续禁止使用std::vector,因为在内部它基本上就是一对指针。 - Sergey Kalinichenko
如果我在这个前向声明和进一步定义struct R { int payload; V v; };之间使用不完整类型struct R;,并且将using V = std::shared_ptr< std::vector< R > >;用作其类型,那么它是否仍然是未定义行为?如果是,那么在定义递归的struct时,我应该始终使用原生指针吗? - Tomilov Anatoliy
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接