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

2021-12-13 00:00:00 templates c++ stl incomplete-type

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

Sometimes it's useful to instantiate a standard container with an incomplete type to obtain a recursive structure:

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] 下有一点,对其他函数的要求":

This tends to work because containers don't have members of type value_type or member functions that pass or return any value_type objects by value. The Standard doesn't seem to say very much about incomplete template arguments, but there is one bit under C++11 §17.6.4.8 [lib.res.on.functions], "requirements on other functions":

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

In particular, the effects are undefined in the following cases: … if an incomplete type (3.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component.

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

Does this make the above constructs illegal, even though the instantiations are not in block scope? Does this fall under "operations on types used to instantiate standard library template components" (also 17.6.4.8)? Or is a library implementation forbidden to incur template instantiations that might fail for incomplete types when all specifically required instantiations succeed?

由于只有函数可以调用和实例化其他函数,将对类型的操作……"限制在块范围内似乎会使成员函数的内容比内容更严格签名和成员类定义.毕竟,在类型完成之前,使用 multi_tree_node 做任何事情当然没有意义.这扩展到 std::unique_ptr,它明确支持不完整的类型参数,即使在块作用域中使用.

Since only functions may call and instantiate other functions, restricting "operations on types…" to those in block scope would seem to hold the contents of member functions to a stricter requirement than the contents of signatures and member class definitions. After all, it certainly doesn't make sense to do anything with a multi_tree_node until the type is complete. And this extends to the std::unique_ptr which explicitly supports an incomplete type argument, even when used in block scope.

编辑 2: 对我来说是正确的,因为我不费心去测试 trie_node 示例――我以前什至尝试过它.这与@Ise 链接的文章中的破损示例相同.然而,虽然这篇文章似乎理所当然地认为没有这样的东西行得通",但解决方案对我来说似乎很简单――std::map 的内部 tree_node 类应该是非成员模板,而不是成员非模板类.

Edit 2: Serves me right for not bothering to test the trie_node example ― and I've even tried it before. It's the same as the example of breakage in the article that @Ise linked. However, while the article seems to take for granted that "nothing like that could work," the solution seems simple to me ― std::map's internal tree_node class should be a non-member template, not a member non-template class.

无论如何,那篇文章很好地确立了设计意图,所以我想我在功能需求"副标题下的挑剔仅此而已.

Anyway, that article establishes design intent pretty well, so I guess my nitpick about being under the subheading of "requirements on functions" is only just that.

推荐答案

个人感觉17.6.4.8/2中instantiating的措辞有点模棱两可,但根据这篇文章,标准的意图似乎不允许使用递归数据类型标准容器.

Personally, I feel the wording instantiating in 17.6.4.8/2 is a little ambiguous, but according to this article, the standard's intent seems not to allow recursive data type using standard containers.

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

On a related note, VC2005 issues an error for class C { std::deque< C > x; };, while it compiles class C { std::vector< C > x; };...
However, in my understanding, this restriction is just for expanding the freedom of the implementation of standard containers. So as Kerrek SB mentioned, there can be containers which allow recursive data structure, and Boost.Container seems to provide this facility.

相关文章