is_nothrow_default_constructible具有noexcept(false)默认构造函数
is_nothrow_default_constructible with a noexcept(false) default constructor
当我偶然发现一个奇怪的行为时,我正试图static_assert
一些类型特征,以确保自定义类型具有预期的noexcept
保证。上面的缩减片段说明了这个问题:
struct DefaultOnly
{
constexpr DefaultOnly() noexcept(false) {};
};
static_assert(std::is_nothrow_default_constructible_v<DefaultOnly>);
对于这个简单的类型,GCC 8通过了static_assert
,而Clang 7失败了。我不知道哪个编译器是正确的。这是其中一个编译器中的错误吗?还是另一行默认构造的标准定义足够灵活,以便两个编译器根据对标准的解释产生有效但不同的结果?
这个问题与noexcept
规范的构造函数没有直接关系,而是与noexcept
运行时编译器如何处理常量表达式有关。
如果您将构造函数声明为无constexpr
,那么两个编译器都可以按预期工作:
struct DefaultOnly
{
DefaultOnly() noexcept(false) {};
};
static_assert(std::is_nothrow_default_constructible_v<DefaultOnly>);
回到C++11,常量表达式对于noexcept
规范来说是不明智的,但它在C++17之前经历了一些变化。到目前为止,CCD_ 8的功能受到CCD_ 9规范的影响。
Clang按预期工作。
以下代码将显示与您的行为相同的行为:
constexpr int foo() noexcept(false) { return 0;}
static_assert(noexcept(foo()));
作为参考,这是GCC-87603报告的摘录:
CWG 1129(最终在C++11中完成)为noexcept添加了一个特殊情况,用于常量表达式,因此:
constexpr void f() {} static_assert(noexcept(f()));
CWG1351(最终以C++14结束)显著地改变了措辞,但特殊情况仍然以不同的形式存在。
P0003R5(最终在C++17中结束)再次更改了措辞,但特殊情况被删除了(意外),所以现在:
constexpr void f() {} static_assert(!noexcept(f()));
根据Richard Smith在LLVM 15481中的说法,CWG对此进行了讨论,但决定保持原样。目前,clang对C++17做了正确的事情(但对C++14和C++11却故意失败)。然而,g++已经实现了C++11的特殊情况,但没有实现C++17的更改。目前,icc和msvc的行为似乎与g++类似。
另请参阅GCC-86044,GCC-88453更具体地等效于您的案例。
在C++17中,Clang是对的。在此之前,constexpr
会覆盖noexcept(false)
,因为noexcept
运算符对于常量表达式总是返回true。
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>()...);
因此,根据标准,如果上述声明语句在noexcept
运算符的意义上"已知不会抛出异常",则std::is_nothrow_default_constructible_v
应为true
。来自[expr.unary.noexcept/3]
noexcept
运算符的结果为true
,除非表达式可能引发。
根据〔except.spec〕/6:
表达式
e
在[…]
e
隐式调用一个可能抛出的函数(例如重载运算符、新表达式中的分配函数、函数参数的构造函数,或者如果e是完整表达式则为析构函数),或者[…]
现在,标准中的措辞有点不精确。std::is_nothrow_default_constructible_v
的值是根据声明语句是否"已知不会抛出异常"来指定的,我们将参考noexcept
运算符的规范来了解这意味着什么。然而,noexcept
运算符只关心表达式,而我们要处理的是声明语句。因此,我们只能猜测,对于声明语句,与潜在抛出表达式的规范等效的内容是什么。我的解释是,在您的情况下,该标准的目的是要求std::is_nothrow_default_constructible_v
为false
,因为规范中给出的声明语句将隐式调用具有潜在抛出异常规范的构造函数…
- "error: no matching function for call to"构造函数错误
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 选择要调用的构造函数
- 如何委托派生类使用其父构造函数?
- 构造函数正在调用一个使用当前类类型的函数
- 没有用于初始化C++中的变量模板的匹配构造函数
- 初始化具有非默认构造函数的std::数组项的更好方法
- 当从函数参数中的临时值调用复制构造函数时
- 在c++构造函数中使用随机字符串生成器
- 一对向量构造函数:初始值设定项列表与显式构造
- 从构造函数抛出异常时如何克服内存泄漏
- 调用 setVisible(false) 在 QWidget 的构造函数中不起作用
- is_nothrow_default_constructible具有noexcept(false)默认构造函数
- 通过常量字符 * 构造函数将 false 转换为对象
- 布尔值被初始化为true,即使构造函数被传递为false
- 为什么std::vector使用move构造函数,尽管声明为noexcept(false)
- unordered_map的默认构造函数,bool类型将设置为false