c++ - 为什么默认 noexcept 移动构造函数被接受?
问题描述
假设以下 c++17 代码:
#include <type_traits>
namespace dtl
{
struct One
{
explicit One(int);
~One() = default;
One(const One &) = delete;
auto operator=(const One &) -> One & = delete;
auto operator=(One &&) -> One & = delete;
One(One &&); // Throwable, not default;
int m_int;
};
struct Two
{
explicit Two(int);
~Two() = default;
Two(const Two &) = delete;
auto operator=(const Two &) -> Two & = delete;
auto operator=(Two &&) noexcept -> Two & = delete;
Two(Two &&) noexcept = default;
One m_one;
};
One::One(One &&) { throw 1; }
static_assert(std::is_nothrow_move_constructible_v<Two>);
}
在这里我们清楚地看到类的移动构造函数One
没有标记为noexcept。类Two
有一个默认的移动构造函数,它被明确要求为 noexcept。
如果我们检查此代码,它会使用 GCC 主干、Clang 主干、MSVC 19.28 进行编译,但使用 MSVC19.24 会失败。
我检查了以下似乎告诉我Two
需要删除移动构造函数的来源:
- gcc接受的带有“noexcept”构造函数的程序,被clang拒绝
- 'noexcept = default' 编译错误
- 错误 35204 - 显式默认默认构造函数的 std::chrono 异常规范与计算出的不匹配
阅读CWG 问题 1778 (N4296 [dcl.fct.def.default]/p3):
如果显式默认的函数使用与隐式声明上的异常规范不兼容 (15.4) 的异常规范声明,则
如果函数在其第一个声明中显式默认,则将其定义为已删除;否则,程序格式错误。
基于这些信息,我只能得出结论,所有 3 个编译器在考虑Two
为 no_throw_move_constructible 时都是错误的,并且应该隐式删除移动构造函数。由于这三个人都忽略了这个标准很奇怪,我想知道:这真的是一个编译器错误还是我错过了什么。
解决方案
我相信您正在查看过时的信息。DR1778 已被P1286R2取代。如果您查看实施状态,您会看到 gcc 10 和 clang 9 实施了这个新的解决方案。
事实上,如果你回到 Godbolt 中的旧 gcc 版本,它会告诉你:
<source>: In function 'int main()':
<source>:35:25: error: use of deleted function 'dtl::Two::Two(dtl::Two&&)'
35 | auto b = std::move(a);
| ^
<source>:23:7: note: 'dtl::Two::Two(dtl::Two&&) noexcept' is implicitly deleted because its exception-specification does not match the implicit exception-specification ''
23 | Two(Two &&) noexcept = default;
| ^~~
Compiler returned: 1
你可以在这里找到 gcc 的讨论。根据这个列表,P1286R2 被接受为 DR,这意味着它被追溯应用于以前的标准。因此,较新的编译器将以您注意到的方式运行,独立于所选的 C++ 标准。
但是,在运行时,这将按预期失败:
dtl::One::One(int) {};
dtl::Two::Two(int) : m_one(0) {};
int main() {
auto a = dtl::Two{1};
try {
auto b = std::move(a);
} catch (...) {
// Even though an exception is thrown, it will not be caught here because
// we broke our `noexcept` promise.
std::cout << "caught" << std::endl;
}
return 0;
}
[:~/tmp] $ /usr/local/Cellar/llvm/11.0.0/bin/clang++ -std=c++17 mv.cpp && ./a.out
libc++abi.dylib: terminating with uncaught exception of type int
Abort trap: 6
推荐阅读
- java - how to test the following Java method
- c# - 有没有办法检测我的 Unity 2D 项目的其他地方是否发生了事件?
- javascript - Tabulator - ajax,如何恢复表头持久性
- r - 使用 ggplot2 或 ggpubr 的不同个体及其散点图之间的相关性
- azure - 调整 Azure Linux VM - redhat
- javascript - 连接具有不同链接的两个字符串
- list - 在 SASS 循环中为当前列表项加上引号
- json - 如何将数据从 json 文件导入到 mongodb atlas 集合
- sql - 报表生成器:如何隐藏列标题,这样我就没有多余的空格
- pine-script - 趋势切换后需要一次警报条件的支持