标准库容器和不完整类型有哪些规则?

9

给定不完整的类型:

struct S; 

接下来的声明如下:

S *p;            // ok, pointer to incomplete types is allowed

std::deque<S> l;  // error, instantiating std::deque with incomplete type is UB

那么以下声明呢?

std::deque<S> *p;   // seems to be UB like the previous case, 
                   // but is it ok if p is not used till S is defined?

std::deque<S*> p;   // not really sure about this one

编辑:该问题使用的是std::list而不是std::deque,但这违背了问题的目的,因为std::list明确地允许使用不完整类型。std::deque似乎没有这样的权限

我认为 std::list<S*> p; 是可以的。编译器生成一个指向结构体的指针而不需要其布局/大小应该不是问题。 - Tony
1
这是https://dev59.com/x1MI5IYBdhLWcg3wIn1m的重复内容。 - Language Lawyer
@LanguageLawyer 不,我特意选择了list,因为我认为它没有使用不完整类型的权限。但是eeorika的答案表明它是可以的。那我得编辑一下问题了。 - cigien
@LanguageLawyer 编辑了问题,使用了一个不允许不完整类型的容器(据我所知)。 - cigien
1个回答

8
std::deque<S> *p;   // seems to be UB like the previous case, 
                   // but is it ok if p is not used till S is defined?
这里其实很有趣。是的,在使用不完整类型实例化容器时是不允许的,也没有相关规定。但问题在于它是否真正被实例化。根据核心语言,它并不需要被实例化。
除非类模板特化已经被显式实例化或显式专门化,否则当该特化在要求完全定义的对象类型的上下文中被引用或者当类类型的完整性影响程序语义时,类模板特化会被隐式实例化。
指向类型的指针并不要求类型是完整的。因此,仅凭这个声明通常不足以引起类模板的实例化,因此确定容器的要求是否被违反可能为时过早。
当然,除非我们认为“类类型的完整性影响程序语义”包括标准库中的合同违规。我想一些实现可能会在这里进行实例化。然而,我不知道任何实现会这样做,因此这可能不是所需的解释。
因此,为了谨慎起见,我认为这也是UB。
std::deque<S*> p;  // not really sure about this one
这很好。无论 S 是否完整,S* 仍然是一个完整的对象类型。我之所以这么说是因为它没有被包含在

[basic.types]

5 已声明但未定义的类,在某些上下文中的枚举类型([dcl.enum]),或未知大小或未完成元素类型的数组,都是不完全定义的对象类型。不完全定义的对象类型和 cv void 是不完全类型([basic.fundamental])。不能将对象定义为不完全类型。

的约束范围内。
对于 S 完整性的限制只出现在尝试在表达式中对这样的指针进行解引用或指针算术运算时。但指针类型本身仍然是完整的。因此,它是容器类型的有效模板参数。

1
我不确定你所说的“过早确定”的意思是什么。这是否肯定可以,还是实现可以拒绝它? - cigien
@cigien - 嗯,我认为“当类类型的完整性影响程序语义时”是相当广泛的。有人可以争论说此类型的完整性会影响程序的语义。 - StoryTeller - Unslander Monica
我肯定认为程序的正确性与程序的语义有关。在我看来,措辞意味着这是可以接受的,但这主要是我的猜测。 - cigien
@cigien - 好的,我在最后一次编辑中总结了我的思考。 - StoryTeller - Unslander Monica
1
谢谢,这样更好。我总是更喜欢那些有立场的答案,无论对错 :) - cigien

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