我试图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的标准定义是否足够灵活,以使两个编译器根据其对标准的解释都能得出有效但不同的结果?
答案 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 ++。
答案 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
。
如果
表达式
true
可能会被抛出[…]
e
隐式调用一个函数(例如,重载运算符, new-expression 中的分配函数,函数参数的构造函数或destructor(如果e)是一个可能会抛出的完整表达式,或者[…]
现在,标准中的措辞恕我直言有点不精确。 e
的值是根据声明语句“是否已知不会引发异常”指定的,我们参考了std::is_nothrow_default_constructible_v
运算符的规范以了解其含义。但是,noexcept
运算符仅与表达式有关,而给予我们的是 declaration-statement 。因此,我们还可以猜测对于 declaration-statement 来说,与可能抛出的 expression 的规范等效的外观。我的解释是,在您的情况下,标准的目的是要求noexcept
为std::is_nothrow_default_constructible_v
,因为规范中给出的 declaration-statement 会隐式调用带有以下内容的构造函数: 可能抛出的异常规范…