【发布时间】:2012-09-27 17:07:43
【问题描述】:
我写了this article,上面写了一些让我很困惑的cmets。
这基本上归结为我看到T2仅用作模板参数,并错误地得出结论,因此我可以借此机会前向声明:
struct T2;
struct T1
{
std::auto_ptr<T2> obj;
};
如果我不继续在同一个 TU 中的某处定义 T2,这将调用 UB,因为 std::auto_ptr<T2> 在其内部 T2* 和 calling delete on an pointer to an object of an incomplete type whose complete type has a non-trivial destructor is undefined 上调用 delete:
[C++11: 5.3.5/5]:如果要删除的对象在删除时具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为未定义。
我碰巧使用的 GCC 工具链 — v4.3.3 (Sourcery G++ Lite 2009q1-203) — 很友好地告诉我:
注意:析构函数和特定于类的操作符 delete 都不会被调用,即使它们是在定义类时声明的。
尽管在其他 GCC 版本中似乎很难获得此诊断信息。
我的抱怨是,如果 deleteing 指向不完整类型实例的指针是格式错误而不是 UB,那么发现这样的错误会容易得多,但是对于实现来说似乎是一个难以解决的问题,所以我明白为什么它是 UB。
但后来有人告诉我,如果我改用std::unique_ptr<T2>,这将是安全且合规的。
n3035 据称在 20.9.10.2 说:
unique_ptr的模板参数T可能是不完整的类型。
我只能在 C++11 中找到:
[C++11: 20.7.1.1.1]:/1 类模板
default_delete用作类模板unique_ptr的默认删除器(销毁策略)。/2
default_delete的模板参数T可能是不完整的类型。
但是,default_delete 的 operator() 确实需要完整的类型:
[C++11: 20.7.1.1.2/4]:如果T是不完整的类型,则程序格式错误。
我想我的问题是这样的:
我的文章的评论者是否正确地说仅由以下代码组成的翻译单元是格式良好且定义良好的?还是他们错了?
struct T2;
struct T1
{
std::unique_ptr<T2> obj;
};
如果它们是正确的,那么编译器应该如何实现这一点,因为它有充分的理由成为 UB,至少在使用 std::auto_ptr 时?
【问题讨论】:
-
因为措辞是“程序格式错误”(没有明确的“并且不需要诊断”),而不是例如'这会导致未定义的行为',这确实意味着需要进行诊断(可以实现为
static_assert( sizeof(T), "" ))。所以是的,std::unique_ptr<T>比std::auto_ptr<T>更安全。 (但请注意,这是由于使用了默认的std::default_delete<T>删除器。) -
我想明确指出,“代码格式正确且定义明确”/“存在 UB”并不是仅有的两种可能结果。 (这可能不是您想要传达的,但我宁愿明确。)
标签: c++ c++11 language-lawyer unique-ptr auto-ptr