我有一个累积当前异常的变量,并且需要在引发当前异常时进行清理(以便不会再次报告相同的错误)。问题是throw std::move(ex);
不调用移动构造函数(这会清理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 Update 5。
我是否做错了什么,因此移动构造函数不会被调用?是否可以抛出异常,以便 C++ 中用于异常存储的临时对象是移动构造的,而不是从原始对象复制构造的?
我试图避免的是双重复制:构建一个副本tmt1
,然后清洁原件,然后使用副本throw
语句,这将构造另一个副本用于临时存储。
编辑:上面的代码示例在 MSVC++2013 Update 5 上给出以下输出
Copy
Caught Test1
虽然预期输出是
Move
Caught Test1
EDIT2:提交了编译器错误报告https://connect.microsoft.com/VisualStudio/feedback/details/1829824
这是一个 MSVC 错误。来自[例外.抛出]:
抛出异常会复制初始化 (8.5, 12.8) 一个临时对象,称为异常对象.
这意味着我们这样做:
ThrowMoveTest __exception_object = move(tmt1);
这肯定应该调用移动构造函数。
请注意,move
这是不必要的,也是有害的。 [class.copy] 规定可以省略复制/移动构造
— in a 抛出表达式(5.17),当操作数是非易失性自动对象的名称时(除了
函数或 catch 子句参数),其范围不超出最内层的末尾
封闭 try 块(如果有),从操作数到异常的复制/移动操作
可以通过将自动对象直接构造到异常对象中来省略对象(15.1)
就这么简单throw tmt1;
会允许tmt1
直接构造到异常对象中。尽管 gcc 和 clang 都没有这样做。
即使复制/移动没有被删除:
当满足复制/移动操作的省略标准,但不满足异常声明时,并且
要复制的对象由左值[...]重载决策指定
首先执行为副本选择构造函数的操作,就好像该对象是由右值指定的一样。
So throw tmt1;
仍然会移动构造异常对象。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)