Подтвердить что ты не робот

Зачем бросать локальную переменную invokes move constructor?

Недавно я "играл" с 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
}
4b9b3361

Ответ 1

В данном случае это, вероятно, ошибка компилятора, поскольку после этого ссылается на переменную, которая была выбрана (и перемещена из нее).

В общем случае вызов move throw концептуально совпадает с перемещением на return. Хорошо вызывать движение автоматически, когда известно, что переменная не может быть указана после данной точки (throw или return).

Ответ 2

Стандарт С++ говорит (15.1.3):

Выбрасывание исключения copy-initializes (8.5, 12.8) временного объекта, называемого объектом исключения. Временной является значением lvalue и используется для инициализации переменной, указанной в соответствующем обработчике (15.3).

Этот пункт может быть также уместен здесь (12.8.31):

Когда определенные критерии выполняются, реализации разрешено опускать конструкцию копирования/перемещения объекта класса, даже если конструктор, выбранный для операции копирования/перемещения и/или деструктор объекта, имеет побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной операции копирования/перемещения как просто два разных способа обращения к одному и тому же объекту и уничтожение этого объекта происходит в более поздние времена, когда два объекта были бы уничтожены без оптимизации. Это разрешение операций копирования/перемещения, называемое копированием, разрешено в следующих случаях (которые могут быть объединены для устранения нескольких копий):


(...)


- в выражении throw, когда операндом является имя энергонезависимого автоматического объекта (отличного от параметра функции или параметра catch-clause) , объем которого не распространяется за пределы (если есть один), операция копирования/перемещения из операнда в объект исключения (15.1) может быть опущена путем создания автоматического объекта непосредственно в объект исключения

Проверено в Visual Studio 2012, эффект:

Exception!
blabla

Похоже на ошибку в GCC.