19

http://www.drdobbs.com/cpp/practical-c-error-handling-in-hybrid-env/197003350?pgno=4

在这篇文章中,Herb Sutter 解释了抛出异常需要一个异常的副本,因为它是作为临时创建的,因此使用 anstd::auto_ptr来绕过复制开销。鉴于 C++11 中提供了移动语义,这还有必要吗?

4

3 回答 3

15

我刚刚检查过,标准允许

  • 省略将 throw 表达式的操作数指定的对象复制或移动到异常对象中
  • 如果您不以其他方式更改程序的含义(即,如果您将重新抛出并且后续捕获会突然看到已更改的异常对象,则省略异常对象的复制或移动到与异常对象相同类型的 catch 子句变量中由前一个 catch 块更改)。

由于允许这些省略,规范要求首先将复制或移动的来源视为右值。所以这意味着如果可能的话,相应的对象被移动。当然,复制和移动省略仍然可以作为首选。


更新

我被告知,将 catch 子句参数的异常对象初始化程序作为右值初始化程序的考虑可能会从标准中删除(因为通常不可能在所有情况下检测到省略时程序的行为何时保持不变复制/移动),所以我建议不要依赖这个(上面的第二个项目符号)。

您仍然可以依赖的是将局部变量移动到异常对象中,如throw x;(上面的第一个项目符号)。

于 2012-08-25T14:25:03.847 回答
2

从异常对象移动现在不是强制性的。

这是C++11的一个缺陷。参见CWG1493

于 2014-04-25T17:03:39.320 回答
0
  • 在 throw 表达式时,总是需要创建异常对象的副本,因为原始对象在堆栈展开过程中超出范围。
  • 在初始化期间,我们可能会期望复制省略(参见this)——省略复制或移动构造函数(对象直接构造到目标对象的存储中)。
  • 但是,即使可能会或可能不会应用复制省略,您也应该提供正确的复制构造函数和/或移动构造函数,这是 C++ 标准要求的(参见 15.1)。请参阅下面的编译错误以供参考。

但现代 C++ 提供了更多功能:“使用移动语义和异常安全移动”

struct demo
{
    demo() = default;
    demo(const demo &) { cout << "Copying\n"; }
    // Exception safe move constructor
    demo(demo &&) noexcept { cout << "Moving\n"; }
private:
    std::vector<int>    m_v;
};
int main()
{
    demo obj1;
    if (noexcept(demo(std::declval<demo>()))){  // if moving safe
        demo obj2(std::move(obj1));             // then move it
    }
    else{
        demo obj2(obj1);                        // otherwise copy it
    }
    demo obj3(std::move_if_noexcept(obj1));     // Alternatively you can do this----------------
    return 0;
}
  • 我们可以使用noexcept(T(std::declval<T>()))来检查是否T存在移动构造函数,并且是noexcept为了决定是否要T通过移动T(使用std::move)的另一个实例来创建一个实例。
  • 或者,我们可以使用std::move_if_noexcept,它使用noexcept运算符并强制转换为右值或左值。此类支票用于std::vector其他容器。
  • 当您处理不想丢失的关键数据时,这将很有用。例如,我们有从服务器接收到的关键数据,我们不想在处理过程中不惜一切代价丢失它。在这种情况下,我们应该使用std::move_if_noexceptwhich 将仅移动关键数据的所有权并且仅当移动构造函数是异常安全的。

来自:C++ 中异常处理的 7 个最佳实践

于 2019-11-09T08:07:45.113 回答