为什么 std::exception 没有移动构造函数?
Why doesn't std::exception have a move constructor?
关于 C++ 异常的最佳实践似乎是 throw by value, catch by reference. I had a look at <exception>
and cppreference,我看到 std::exception
有一个复制构造函数但没有移动构造函数,这是为什么?有一个移动构造函数不会允许廉价地按值捕获从而简化指南吗?
我检查了几个编译器,显然 try/catch
机制不使用移动语义。
#include <iostream>
using namespace std;
struct err {
err() { cout << "created err" << endl; }
err(const err&) { cout << "copied err" << endl; } // Compilation error if this is commented out
err(err&&) noexcept { cout << "moved err" << endl; }
~err() { cout << "destroyed err" << endl; }
};
void foo() {
throw err{};
}
int main() {
try {
cout << "calling foo" << endl;
foo();
cout << "called foo" << endl;
}
catch (err e) {
cout << "caught err" << endl;
}
}
输出:
calling foo
created err
copied err
caught err
destroyed err
destroyed err
所以拥有移动构造函数将毫无意义。
为什么会这样可能是另一个问题:)
std::exception
的后代拥有自己的数据。例如 std::runtime_error
拥有其 what()
消息。并且该消息是动态分配的,因为它可以是任意长的消息。
但是,复制构造函数被标记为 noexcept
(隐含地),因为 std::exception
复制构造函数是 noexcept
.
#include <stdexcept>
#include <type_traits>
int
main()
{
static_assert(std::is_nothrow_copy_constructible<std::runtime_error>{});
}
仅 class 拥有动态分配的消息并具有 noexcept
复制构造函数的方法是共享所有权(引用计数)。所以 std::runtime_error
本质上是一个常量,引用计数字符串。
根本没有动力为这些类型提供移动构造函数,因为复制构造函数不仅已经非常快,而且程序的异常路径仅在异常情况下执行。 std::runtime_error
的移动构造函数唯一能做的就是消除原子 increment/decrement。没人关心。
Wouldn't having a move-constructor allow cheaply catching by value and thus simplifying the guidelines?
你已经可以按价值便宜地捕捞了。但准则存在是因为异常通常是继承层次结构的一部分,按值捕获会分割异常:
#include <exception>
#include <iostream>
#include <stdexcept>
int
main()
{
try
{
throw std::runtime_error("my message");
}
catch (std::exception e)
{
std::cout << e.what() << '\n';
}
}
输出(对我来说):
std::exception
关于 C++ 异常的最佳实践似乎是 throw by value, catch by reference. I had a look at <exception>
and cppreference,我看到 std::exception
有一个复制构造函数但没有移动构造函数,这是为什么?有一个移动构造函数不会允许廉价地按值捕获从而简化指南吗?
我检查了几个编译器,显然 try/catch
机制不使用移动语义。
#include <iostream>
using namespace std;
struct err {
err() { cout << "created err" << endl; }
err(const err&) { cout << "copied err" << endl; } // Compilation error if this is commented out
err(err&&) noexcept { cout << "moved err" << endl; }
~err() { cout << "destroyed err" << endl; }
};
void foo() {
throw err{};
}
int main() {
try {
cout << "calling foo" << endl;
foo();
cout << "called foo" << endl;
}
catch (err e) {
cout << "caught err" << endl;
}
}
输出:
calling foo
created err
copied err
caught err
destroyed err
destroyed err
所以拥有移动构造函数将毫无意义。
为什么会这样可能是另一个问题:)
std::exception
的后代拥有自己的数据。例如 std::runtime_error
拥有其 what()
消息。并且该消息是动态分配的,因为它可以是任意长的消息。
但是,复制构造函数被标记为 noexcept
(隐含地),因为 std::exception
复制构造函数是 noexcept
.
#include <stdexcept>
#include <type_traits>
int
main()
{
static_assert(std::is_nothrow_copy_constructible<std::runtime_error>{});
}
仅 class 拥有动态分配的消息并具有 noexcept
复制构造函数的方法是共享所有权(引用计数)。所以 std::runtime_error
本质上是一个常量,引用计数字符串。
根本没有动力为这些类型提供移动构造函数,因为复制构造函数不仅已经非常快,而且程序的异常路径仅在异常情况下执行。 std::runtime_error
的移动构造函数唯一能做的就是消除原子 increment/decrement。没人关心。
Wouldn't having a move-constructor allow cheaply catching by value and thus simplifying the guidelines?
你已经可以按价值便宜地捕捞了。但准则存在是因为异常通常是继承层次结构的一部分,按值捕获会分割异常:
#include <exception>
#include <iostream>
#include <stdexcept>
int
main()
{
try
{
throw std::runtime_error("my message");
}
catch (std::exception e)
{
std::cout << e.what() << '\n';
}
}
输出(对我来说):
std::exception