is_nothrow_default_constructible与noexcept(false)默认构造函数

时间:2018-12-23 01:47:31

标签: c++ language-lawyer

我试图static_assert的一些类型特征,以确保当我偶然发现奇怪的行为时,自定义类型具有预期的noexcept保证。上面减少的代码段说明了此问题:

struct DefaultOnly
{
    constexpr DefaultOnly() noexcept(false) {};
};

static_assert(std::is_nothrow_default_constructible_v<DefaultOnly>);

对于这种简单类型,GCC 8传递了static_assert,而Clang 7失败了。我不知道哪个编译器是正确的。这是其中一个编译器中的错误吗?或者 nothrow default的标准定义是否足够灵活,以使两个编译器根据其对标准的解释都能得出有效但不同的结果?

3 个答案:

答案 0 :(得分:4)

此问题与具有noexcept规范的构造函数没有直接关系,但是与noexcept起作用时,编译器如何处理常量表达式

如果您将构造函数声明为否constexpr,则两个编译器都将按预期工作:

struct DefaultOnly
{
    DefaultOnly() noexcept(false) {};
};
static_assert(std::is_nothrow_default_constructible_v<DefaultOnly>);

回到C ++ 11以来,常量表达式noexcept规范并不敏感,但是直到C ++ 17才经历了变化。到目前为止,constexpr函数受noexcept规范的影响。

C语按预期工作。

以下代码将显示与您相同的行为:

constexpr int foo() noexcept(false) { return 0;}
static_assert(noexcept(foo()));

作为参考,这是GCC-87603报告的摘录:

  

CWG 1129(最终出现在C ++ 11中)为no添加了一种特殊情况,除了常量表达式,因此:

     

constexpr void f() {} static_assert(noexcept(f()));

     

CWG 1351(最终出现在C ++ 14中)大大改变了措词,但特殊情况仍然以另一种形式出现。

     

P0003R5(最终出现在C ++ 17中)再次更改了措辞,但特殊情况被删除(偶然),所以现在:

     

constexpr void f() {} static_assert(!noexcept(f()));

     

根据LLVM 15481中的Richard Smith,CWG对此进行了讨论,但决定保持原样。当前,clang对C ++ 17做正确的事情(故意使C ++ 14和C ++ 11失败)。但是,g ++已经实现了C ++ 11的特殊情况,但没有实现C ++ 17的更改。目前,icc和msvc的行为似乎类似于g ++。

另请参阅GCC-86044,而GCC-88453更具体地等同于您的情况。

答案 1 :(得分:3)

从C ++ 17开始,Clang是正确的。在此之前,constexpr会覆盖noexcept(false),因为noexcept 操作符总是returned true for constant expressions

答案 2 :(得分:1)

std::is_nothrow_default_constructible_v<T>等效于std::is_nothrow_constructible<T>::value,在[meta.unary.prop]中指定为

  

is_­constructible_­v<T, Args...>true,并且is_constructible的变量定义(如下定义)已知不会引发任何异常([expr.unary.noexcept])。

有问题的变量定义在[meta.unary.prop]/8

中给出
  

当且仅当对于某些发明变量is_­constructible<T, Args...>遵循以下变量定义时,才应满足模板专业化t的谓词条件:

T t(declval<Args>()...);

因此,按照标准,如果上述声明声明“已知不会抛出异常”,则std::is_nothrow_default_constructible_v应该是true {1}}运算符。来自[expr.unary.noexcept/3]

  

除非表达式可能被抛出,否则noexcept运算符的结果为noexcept

根据[except.spec]/6

  

如果

表达式 true可能会被抛出

     

[…]

     
      
  • e隐式调用一个函数(例如,重载运算符, new-expression 中的分配函数,函数参数的构造函数或destructor(如果e)是一个可能会抛出的完整表达式,或者

         

    […]

  •   

现在,标准中的措辞恕我直言有点不精确。 e的值是根据声明语句“是否已知不会引发异常”指定的,我们参考了std::is_nothrow_default_constructible_v运算符的规范以了解其含义。但是,noexcept运算符仅与表达式有关,而给予我们的是 declaration-statement 。因此,我们还可以猜测对于 declaration-statement 来说,与可能抛出的 expression 的规范等效的外观。我的解释是,在您的情况下,标准的目的是要求noexceptstd::is_nothrow_default_constructible_v,因为规范中给出的 declaration-statement 会隐式调用带有以下内容的构造函数: 可能抛出的异常规范