非平凡的析构函数使得类非平凡可构造

Ori*_*ent 16 c++ destructor

考虑以下代码:

#include <type_traits>

struct T {};

static_assert(std::is_trivially_destructible< T >{});
static_assert(std::is_trivially_default_constructible< T >{});

struct N { ~N() { ; } };

static_assert(!std::is_trivially_destructible< N >{});
static_assert(!std::is_trivially_default_constructible< N >{});
Run Code Online (Sandbox Code Playgroud)

它编译好使用clang 3.7.0:实例.但总结标准:

如果满足以下所有条件,则类T的默认构造函数是微不足道的(即不执行任何操作):

  • 构造函数不是用户提供的(即,隐式定义或默认)
  • T没有虚拟成员函数
  • T没有虚拟基类
  • T没有使用默认初始值设定项的非静态成员.(自C++ 11以来)
  • T的每个直接基都都有一个普通的默认构造函数
  • 类类型的每个非静态成员都有一个普通的默认构造函数

正如我所看到的,不依赖于析构函数的琐碎性.

我错过了什么?是clangbug吗?

额外

我找到了一个解决方法:static_assert(__has_trivial_constructor( N ));内置类型特征.有支持clang,gccMSVC.

对于is_noexcept_constructible类型特征的家庭也有解决方法.

Sha*_*our 6

这个问题在LWG问题2116中有所描述:std :: swap noexcept(what?),我们可以从std :: is_trivially_default_constructible的cppreference部分看到这个:

在许多实现中,is_nothrow_default_constructible还检查析构函数是否抛出,因为它实际上是noexept(T()):GCC bug 51452 LWG issue 2116

这看起来只是谈论is_nothrow_default_constructible但如果我们详细阅读这个问题,我们也会看到它也适用于此.

如果我们遵循gcc错误报告,也许会更容易:[DR 2116] has_nothrow _.*首先引用的构造函数错误说:

检测不可建造性的特征是错误的,因为它们受到物体是否具有不可靠性的影响; 在noexcept(...)运算符的完整表达式的求值结束时调用destroy.它们都使用构造临时内部noexcept的模式,而它们应该使用placement new

这明确说明了LWG问题中唯一真正提到的内容,最终说:

is_nothrow_constructible是根据 is_constructible定义的,is_constructible是通过查看假设变量并询问变量定义是否已知不会抛出异常来定义的.该问题声称,在给定上下文的情况下,这也会检查类型的析构函数,因此如果析构函数可能抛出则会返回false.如果构造函数为noexcept(true)且析构函数为noexcept(false),则至少有一个实现(Howard's)会返回false.所以这不是一个紧张的解释.问题是要求使用placement new来定义它,而不是根据临时对象来定义,以便更清楚地表明is_nothrow_constructible只查看构造函数的noexcept状态,而不是析构函数.

这也影响std::is_trivially_default_constructible依赖于std :: is_trivially_constructible,它具有相同的作用is_constructible但具有以下进一步的限制:

但变量定义不会调用任何不重要的操作