std::is_nothrow_move_constructible是否需要一个noexcept析构函数?

7
以下代码无法通过Visual Studio 2017 (15.5),gcc 6.4.0和clang 4.0.1的编译,即静态断言失败:
struct Type
{
  Type(Type&&) noexcept {}

  ~Type() noexcept(false) {}
};

static_assert(std::is_nothrow_move_constructible<Type>::value, "Type should be nothrow-move-constructible");
static_assert(std::is_nothrow_constructible<Type, Type&&>::value, "Type should be nothrow-constructible from Type&&");

这是否符合C++标准?std::is_nothrow_move_constructible是否需要一个noexcept析构函数?为什么?
如果我这样使用:
Type a;
Type b(std::move(a));  

a的析构函数在第二个语句中不会被调用。


1
仅供参考:从 http://en.cppreference.com/w/cpp/types/is_move_constructible 开始,经过 std::is_nothrow_move_constructible -> std::is_nothrow_constructible 这条链,你会看到这里的注释:_"在许多实现中,is_nothrow_constructible 还会检查析构函数是否抛出异常..."_,链接为 http://en.cppreference.com/w/cpp/types/is_constructible。 - Richard Critten
@RichardCritten 感谢您的提示!由于这明确引用了“许多实现”,这听起来像是这种行为不符合标准。如果标准将这留给实现者作为选择,我会感到惊讶的。 - Simon
1个回答

2
我们可以看到,std::is_nothrow_move_constructible 是根据 is_­nothrow_­constructible_­v 定义的。因此,它受到了LWG问题2116: is_nothrow_constructible and destructors的限制,因为该问题尚未解决,所以除非问题得到不同的解决方案,否则这不是一个错误。
它以以下讨论开头:

我认为,如果我们指定is_[nothrow_]constructible基于需要可销毁性变量声明的有效性,那么这显然是我们规范中的一个错误,并且未能意识到实际的原始意图。规范应该基于placement-new。

Daniel:在规范时,这是有意的,解决方案并没有通过删除is_constructible的销毁语义来完成。

is_constructible的设计也受到了先前明确包含销毁语义的Constructible概念的影响,因为在库的概念化期间,它使得约束在库中变得简单,因为你不需要一直添加Destructible。它经常被暗示但在C++03中从未被明确提出。

纯构造语义也被认为是有用的,因此HasConstructor也存在,并且作为特征肯定也有用。

另一个经常被忽视的例子是:这也会影响到像pair、tuple、array这样包含多个类型的包装器类型:如果你认为T1具有已删除的析构函数,而T2具有可能引发异常的构造函数,则显然编译器可能需要在std::pair的构造函数中使用T1的析构函数来确保满足核心语言要求(所有先前完全构建的子对象必须被销毁)。

核心语言也在[class.copy]p11中遵守这一事实:

如果X有:
[…]
— 任何直接或虚拟基类或非静态数据成员的类型具有已删除或从默认构造函数中无法访问的析构函数,
[…]
则类X的默认复制/移动构造函数将被定义为已删除(9.4.3 [dcl.fct.def.delete])。

Dave:这是一个关于is_nothrow_constructible的问题。没有noexcept析构函数会破坏它,这是一个缺陷。

最后得出结论:

Ville希望“演进小组”审查此问题。

此gcc错误报告中可以看出,至少gcc将等待问题得到解决。

请参见非平凡析构函数使类不是平凡构造的,该问题涉及到相关问题的其他参考文献。还要注意clang bug for this


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