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

asa*_*elr 19 c++ exception move-semantics c++11

最近,我用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
}
Run Code Online (Sandbox Code Playgroud)

Spo*_*ook 7

C++标准说(15.1.3):

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

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

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

...)

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

在Visual Studio 2012中检查,效果如下:

Exception!
blabla
Run Code Online (Sandbox Code Playgroud)

它看起来像GCC中的一个错误.


Jur*_*aho 5

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

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