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

16
考虑以下代码:
#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 >{});

使用clang 3.7.0编译没有问题:实例。但是根据标准的总结:

如果以下条件均成立,则类T的默认构造函数是平凡的(即不执行任何操作):

  • 构造函数不是用户提供的(即隐式定义或默认定义)
  • T没有虚成员函数
  • T没有虚基类
  • T没有具有默认初始化器的非静态成员。 (自C++11起)
  • T的每个直接基类都有一个平凡的默认构造函数
  • 类类型的每个非静态成员都有一个平凡的默认构造函数

据我所见,这并不依赖于析构函数的平凡性。

我漏掉了什么吗?这是clang的bug吗?

补充说明

我发现了一个解决方法:使用static_assert(__has_trivial_constructor( N ));内置类型特征。clanggccMSVC都支持它。
对于is_noexcept_constructible类型特征系列,也有解决方法

3
本质上与LWG 2116相同。is_meow_constructible被规定为使用变量定义,因此它也考虑了析构函数。 - T.C.
2
现在假设一个倡导现代C++的人,参加了一次代码审查,并发现自己不得不更改已经通过概念验证的概念检查器。(不行,最好不要这样)。@Orient:你的发现很重要! - decltype_auto
有趣的是,VS2015编译器会说N是可以被默认构造的。然而智能感知并不这么认为。 - Weak to Enuma Elish
1个回答

5
这个问题在LWG issue 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 bug report: [DR 2116] has_nothrow_.*constructor bugs的引用,可能会更容易理解。
检测nothrow constructibility的特征是有缺陷的,因为它们受到对象是否具有nothrow dtor的影响;在noexcept(...)操作符的完整表达式评估结束时会调用析构函数。它们都使用在nothrow内部构造临时变量的模式,而应该使用placement new。
这明确说明了LWG问题中只是含蓄地提到的内容:
is_nothrow_constructible是根据is_constructible定义的,后者通过查看假设变量并询问变量定义是否已知不会抛出异常来定义。该问题声称,在给定上下文的情况下,它还会检查类型的析构函数,因此如果析构函数可能引发异常,则会返回false。至少一个实现(Howard's)会在构造函数为noexcept(true)且析构函数为noexcept(false)时返回false。所以这并不是一种牵强的解释。该问题要求将其定义为基于placement new,而不是基于临时对象,以使得is_nothrow_constructible仅考虑构造函数的noexcept状态,而不考虑析构函数。

这也会影响到std::is_trivially_default_constructible,它依赖于std::is_trivially_constructible,后者与is_constructible相同,但有进一步的限制:

但变量定义不调用任何非平凡操作。


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