我有一个变量,它累积当前异常并且需要在当前异常被抛出时被清除(这样就不会再次报告相同的错误)。问题是 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/