标准容器模板可以用不完整的类型实例化吗?

时间:2011-11-30 16:57:52

标签: c++ templates stl incomplete-type

有时,实例化具有不完整类型的标准容器以获取递归结构是有用的:

struct multi_tree_node { // Does work in most implementations
    std::vector< multi_tree_node > child;
};

struct trie_node { // Does not work in most implementations
    std::map< char, trie_node > next;
};

这往往有效,因为容器没有value_type类型的成员或者按值传递或返回任何value_type个对象的成员函数。标准似乎并没有非常详细地说明不完整的模板参数,但在C ++11§17.6.4.8[lib.res.on.functions],“其他函数的要求”下有一点:

  

特别是,在以下情况下效果未定义:...如果在实例化模板组件时将不完整类型(3.9)用作模板参数,除非特别允许该组件。

这是否会使上述构造非法,即使实例化不在块范围内?这属于“用于实例化标准库模板组件的类型的操作”(也是17.6.4.8)吗?或者是一个库实现被禁止引发模板实例化,当所有特别需要的实例化成功时,这些实例化可能会因不完整类型而失败?

编辑:由于只有函数可以调用和实例化其他函数,因此将“对类型的操作...”限制为块范围内的操作似乎会将成员函数的内容保持为比内容更严格的要求签名和成员类定义。毕竟,在类型完成之前,使用multi_tree_node 做任何事情肯定没有意义。这扩展到显式支持不完整类型参数的std::unique_ptr即使在块范围中使用

编辑2:为我提供正确的服务,因为我没有费心去测试trie_node示例 - 我以前也尝过它。它与@Ise链接的the article中的破损示例相同。然而,虽然这篇文章似乎理所当然地认为“没有什么可以起作用”,但解决方案对我来说似乎很简单 - std::map的内部tree_node类应该是非成员模板,而不是成员非模板类。

无论如何,那篇文章很好地建立了设计意图,所以我想我的挑剔是关于“功能要求”的副标题只是那个。

3 个答案:

答案 0 :(得分:11)

这是我尝试解释:

标准只是说你不能这样做,即使任何给定的具体实施可能没有支持这种结构的问题。但想象一下,例如,如果有人想要编写一个“小矢量”优化,通过该优化,矢量总是包含五个元素的空间。你有麻烦,因为你有自我指涉的类型。即使向量采用某种静态分支取决于值类型的大小,这也是一个问题。

因此,为了不排除包含此类构造的实现,标准只是说您必须只使用完整类型。换句话说,大多数容器只包含对值类型的引用或指针的事实是实现细节而不是标准要求。

只是为了澄清这一点:如果你定义自己的类模板,完全有可能以明确支持不完整类型的方式进行设计。标准中的一个示例是std::unique_ptr,它对不完整的类型参数T[](甚至void)非常满意。

答案 1 :(得分:4)

就个人而言,我觉得17.6.4.8/2中的实例化的措辞有点 暧昧,但根据 this article, 标准的意图似乎不允许使用递归数据类型 标准容器。

在相关的说明中,VC2005发出错误 class C { std::deque< C > x; };,而它编译 class C { std::vector< C > x; }; ...
但是,根据我的理解,这个限制只是为了扩大 自由执行标准容器。 因此,正如提到的 Kerrek SB ,可以有允许的容器 递归数据结构,和 Boost.Container 似乎提供了这种便利。

答案 2 :(得分:1)

通常,将不完整类型用作标准库组件的模板参数是UB。这是reference

如果在实例化模板组件或评估概念时将不完整的类型([basic.types])用作模板参数,除非该组件特别允许。

请注意,自c ++ 17起,已向std::vector授予显式权限以允许不完整的类型。这是reference

如果分配器满足分配器完整性要求,则在实例化矢量时可以使用不完整类型T。在引用所得的向量专业化的任何成员之前,T必须完整。

因此,在您的示例中,multi_tree_node格式正确,但是trie_node是UB。