抛出异常时不调用Move构造函数 [英] Move constructor is not called when throwing an exception

查看:160
本文介绍了抛出异常时不调用Move构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a variable which accumulates the current exception and needs to get cleaned when the current exception gets thrown (so that the same error does not get reported again). The problem is that throw std::move(ex); does not call the move constructor (which would clean ex), but rather calls a copy constructor (so that ex stays with the already thrown errors too). A MVCE follows:

#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。

I am using MSVC++2013 Update 5.

有什么我做错了,所以移动构造函数不会被调用这个原因?有没有抛出异常,以便用于C ++中的异常存储的临时对象是移动构建的,而不是从原来的拷贝构造?

Is there something that I am doing wrong so that the move constructor does not get called for this reason? Is there away to throw an exception so that the temporary object used for exception storage in C++ gets move-constructed, rather than copy-constructed from the original?

我试试避免是双重复制:构建 tmt1 的副本,然后清理原件,然后使用 throw

What I try to avoid is double-copying: construction of a copy of tmt1, then cleaning the original, then using the copy in throw statement, which would construct another copy for temporary storage.

编辑:以上代码示例在MSVC ++ 2013 Update 5上提供以下输出

The above code example gives the following output on MSVC++2013 Update 5

Copy
Caught Test1


$ b b

预期输出为

While the expected output is

Move
Caught Test1

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

Submitted a compiler bug report https://connect.microsoft.com/VisualStudio/feedback/details/1829824

推荐答案

这是一个MSVC bug。 from [except.throw]:

This is an MSVC bug. From [except.throw]:


抛出异常副本 - 初始化(8.5,12.8)临时对象,称为异常对象

这意味着我们:

ThrowMoveTest __exception_object = move(tmt1);

这应该肯定会调用move构造函数。

which should definitely call the move constructor.

请注意,这里的 move 是不必要的,也是有害的。 [class.copy]规定可以舍弃复制/移动构造

Note that the move here is unnecessary and also damaging. [class.copy] stipulates that copy/move construction can be elided


5.17),当操作数是一个非易失性自动对象(除
a函数或catch子句参数之外)的名称,该对象的范围不超过最内侧的
结束try-block如果有一个),从操作数到异常
对象(15.1)的复制/移动操作可以通过将自动对象直接构造为异常对象来省略

— 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都不这样做。

So simply throw tmt1; would have allowed for tmt1 to be constructed directly into the exception object. Although neither gcc nor clang do this.

即使复制/移动没有消失:

And even if the copy/move is not elided:


满足复制/移动操作的精度的标准,但不是针对异常声明,并且要复制的
对象由lvalue [...]重载解析
指定以选择构造函数

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; 仍然会移动 - 构造异常对象。

So throw tmt1; would still move-construct the exception object.

这篇关于抛出异常时不调用Move构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆