c++ - 抛出异常时不调用 move 构造函数

标签 c++ c++11 exception move-semantics move-constructor

我有一个变量,它累积当前异常并且需要在当前异常被抛出时被清除(这样就不会再次报告相同的错误)。问题是 throw std::move(ex); 不调用 move 构造函数(这将清除 ex),而是调用复制构造函数(这样 ex 也保留已经抛出的错误)。 MVCE 如下:

#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;

class ThrowMoveTest : exception
{
public:
    ThrowMoveTest(const string& what)
    {
        _what = what;
    }
    ThrowMoveTest(const ThrowMoveTest& fellow)
    {
        cout << "Copy " << what() << endl;
        _what = fellow._what;
    }
    ThrowMoveTest(ThrowMoveTest&& fellow)
    {
        cout << "Move " << what() << endl;
        _what = std::move(fellow._what);
    }
    virtual const char* what() const override
    {
        return _what.c_str();
    }
private:
    mutable string _what;
};

int main()
{
    try
    {
        ThrowMoveTest tmt1("Test1");
        throw move(tmt1);
    }
    catch (const ThrowMoveTest& ex)
    {
        cout << "Caught " << ex.what() << endl;
    }
    return 0;
}

我正在使用 MSVC++2013 更新 5。

是不是我做错了什么导致 move 构造函数没有被调用?有没有办法抛出异常,以便在 C++ 中用于异常存储的临时对象是 move 构造的,而不是从原始对象复制构造的?

我尽量避免的是双重复制:构造一个 tmt1 的拷贝,然后清理原来的,然后在 throw 语句中使用拷贝,这将构造临时存储的另一个拷贝。

编辑:上面的代码示例在 MSVC++2013 Update 5 上给出了以下输出

Copy
Caught Test1

虽然预期的输出是

Move
Caught Test1

EDIT2:提交编译器错误报告 https://connect.microsoft.com/VisualStudio/feedback/details/1829824

最佳答案

这是一个 MSVC 错误。来自 [except.throw]:

Throwing an exception copy-initializes (8.5, 12.8) a temporary object, called the exception object.

这意味着我们这样做:

ThrowMoveTest __exception_object = move(tmt1);

肯定会调用 move 构造函数。


请注意,此处的move 是不必要的,而且还会造成破坏。 [class.copy]规定copy/move构造可以省略

— in a throw-expression (5.17), when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object

所以简单地 throw tmt1; 将允许将 tmt1 直接构建到异常对象中。虽然 gcc 和 clang 都没有这样做。

即使没有省略复制/move :

When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue [...] overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

所以 throw tmt1; 仍然会 move 构造异常对象。

关于c++ - 抛出异常时不调用 move 构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32759433/

相关文章:

c++ - 使用stof引发异常-C++

c++ - Clang 与 GCC : Friend-ing a global function via qualified/unqualified names

c++ - Windows CE 5 和 6 之间的互锁 API

C++ 线程 - 在循环中生成一个对象(类)?

c# - 异常不抛出?

c++ - 如何从 boost 异常中正确解包错误代码

c++ - 如何使用 boost 异常将信息添加到 std::exception

c++ - 不调用线程函数。语法有什么问题吗

c++ - 这个 static_assert 会被触发吗?

c++ - 锁定 shared_ptr