为什么 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