代码之家  ›  专栏  ›  技术社区  ›  Motti

为什么std::exception没有move构造函数?

  •  1
  • Motti  · 技术社区  · 5 年前

    C++中关于异常的最佳实践似乎是 throw by value, catch by reference 我看了一下 <exception> cppreference 我看到了 std::exception 有一个复制构造函数,但没有移动构造函数,这是为什么?如果有一个move构造函数,岂不是可以便宜地按值捕获,从而简化指导方针?

    0 回复  |  直到 5 年前
        1
  •  2
  •   Howard Hinnant    5 年前

    的后裔 std::exception 做自己的数据。例如 std::runtime_error 拥有其 what() 消息。该消息是动态分配的,因为它可以是任意长的消息。

    但是,复制构造函数已标记 noexcept (隐含地)因为 std::异常 复制构造函数是 没有例外 .

    #include <stdexcept>
    #include <type_traits>
    
    int
    main()
    {
        static_assert(std::is_nothrow_copy_constructible<std::runtime_error>{});
    }
    

    这个 只有 类拥有动态分配的消息并拥有 没有例外 复制构造函数,用于共享该所有权(引用计数)。所以 std::runtime_error 本质上是一个常量,引用计数字符串。

    根本没有动力给这些类型一个移动构造函数,因为复制构造函数不仅已经非常快了,而且程序的特殊路径只在特殊情况下执行。关于move构造函数的唯一用途 std::runtime_error 可以做的就是消除原子增量/减量。没有人关心。

    如果有一个move构造函数,岂不是可以便宜地按值捕获,从而简化指导方针?

    你已经可以很便宜地抓住价值了。但该准则的存在是因为异常通常是继承层次结构的一部分,按值捕获会对异常进行切片:

    #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
    
        2
  •  1
  •   Motti    5 年前

    我检查了几个编译器,显然 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;
        }
    }
    

    输出:

    调用foo
    创建错误
    复制错误
    捕获错误
    销毁错误
    销毁错误

    因此,拥有一个移动构造函数将毫无意义。

    为什么会这样可能是另一个问题:)