为什么抛出局部变量调用移动构造函数?

时间:2013-06-05 11:39:51

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

最近,我和rvalues“玩”了解他们的行为。大多数结果并没有让我惊讶,但后来我看到如果我抛出一个局部变量,就会调用移动构造函数。

在那之前,我认为移动语义规则的目的是保证只有当编译器能够检测到它不再被使用时(如在临时对象中),或者对象将移动(并变为无效)。用户承诺不使用它(如在std :: move中)。

但是,在下面的代码中,没有这个条件,并且我的变量仍然被移动(至少在g ++ 4.7.3上)。

为什么?

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

int main() {
    string s="blabla";
    try {
        throw s;
    }
    catch(...) {
        cout<<"Exception!\n";
    }
    cout<<s; //prints nothing
}

2 个答案:

答案 0 :(得分:7)

C ++标准说(15.1.3):

抛出异常复制初始化(8.5,12.8)临时对象,称为异常对象。临时是一个左值,用于初始化匹配处理程序中命名的变量(15.3)。

这一段也可能与此相关(12.8.31):

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使为复制/移动操作选择的构造函数和/或对象的析构函数具有副作用。在这种情况下,实现将省略的复制/移动操作的源和目标视为引用同一对象的两种不同方式,以及对该对象的破坏 发生在两个对象在没有优化的情况下被销毁的时间的晚些时候。复制/移动操作的省略,称为复制省略,在下列情况下是允许的(可以合并以消除多个副本):

(...)

- 在throw-expression 中,当操作数是非易失性自动对象的名称(函数或catch子句参数除外)其范围不超出结束时最里面的封闭try-block(如果有的话),通过将自动对象直接构造到异常对象中,可以省略从操作数到异常对象(15.1)的复制/移动操作

在Visual Studio 2012中检查,效果:

Exception!
blabla

它看起来确实是GCC中的一个错误。

答案 1 :(得分:5)

在给定的情况下,它可能是编译器错误,因为之后引用了抛出(并从中移动)的变量。

一般情况下,调用throw移动在概念上与继续return相同。当知道在给定点(throwreturn)之后无法引用变量时,最好自动调用move。