19

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

在那之前,我认为移动语义规则的目的是保证只有当编译器可以检测到它不再被使用(如在临时对象中),或者用户承诺不使用时,对象才会移动(并变得无效)使用它(如在 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
}
4

2 回答 2

7

C++ 标准说(15.1.3):

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

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

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

(...)

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

签入Visual Studio 2012,效果:

Exception!
blabla

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

于 2013-06-05T11:49:41.450 回答
5

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

在一般情况下,调用 move onthrow在概念上与 move on 相同returnthrow当已知在给定点 (或)之后无法引用变量时,最好自动调用 move return

于 2013-06-05T11:50:11.307 回答