Почему std::move предотвращает RVO?

Во многих случаях при возврате локального из функции, RVO запускается. Однако я думал, что явно используя std::move по крайней мере, принудительное перемещение, когда RVO не происходит, но это RVO все еще применяется, когда это возможно. Однако, похоже, что это не так.

#include "iostream"

class HeavyWeight
{
public:
    HeavyWeight()
    {
        std::cout << "ctor" << std::endl;
    }

    HeavyWeight(const HeavyWeight& other)
    {
        std::cout << "copy" << std::endl;
    }

    HeavyWeight(HeavyWeight&& other)
    {
        std::cout << "move" << std::endl;
    }
};

HeavyWeight MakeHeavy()
{
    HeavyWeight heavy;
    return heavy;
}

int main()
{
    auto heavy = MakeHeavy();
    return 0;
}

Я тестировал этот код с VC++11 и GCC 4.71, отладка и выпуск (-O2) конфиг. Копия ctor никогда не вызывается. Ctor перемещения вызывается только VC++11 в конфигурации отладки. На самом деле, с этими компиляторами все в порядке, но, насколько мне известно, RVO не является обязательным.

Однако, если я явно использую move:

HeavyWeight MakeHeavy()
{
    HeavyWeight heavy;
    return std::move(heavy);
}

ход ctor всегда вызывается. Поэтому попытка сделать его "безопасным" делает его еще хуже.

Мои вопросы:
- Почему std::move предотвратить РВО?
- Когда лучше "надеяться на лучшее" и полагаться на RVO, и когда я должен явно использовать std::move? Или, другими словами, как я могу позволить оптимизации компилятора выполнять свою работу и по-прежнему обеспечивать перемещение, если RVO не применяется?

2 ответа

Решение

Случаи, когда допускается копирование и перемещение, описаны в разделе 12.8 §31 Стандарта (версия N3690):

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

  • в return оператор в функции с типом возвращаемого класса, когда выражение является именем энергонезависимого автоматического объекта (кроме параметра функции или предложения catch) с тем же типом cv-unqualified, что и тип возвращаемого функцией, copy/ Операция перемещения может быть опущена путем создания автоматического объекта непосредственно в возвращаемое значение функции
  • [...]
  • когда временный объект класса, который не был привязан к ссылке (12.2), будет скопирован / перемещен в объект класса с тем же типом cv-unqualified, операция копирования / перемещения может быть опущена путем создания временного объекта непосредственно в цель опущенной копии / перемещения
  • [...]

(Два случая, которые я пропустил, относятся к случаю создания и отлова объектов исключений, которые я считаю менее важными для оптимизации.)

Следовательно, в операторе возврата копирование может произойти, только если выражение является именем локальной переменной. Если ты пишешь std::move(var), тогда это уже не имя переменной. Поэтому компилятор не может исключить этот шаг, если он должен соответствовать стандарту.

Стефан Т. Лававей говорил об этом на Going Native 2013 и объяснил точно вашу ситуацию и почему следует избегать std::move() Вот. Начните смотреть с минуты 38:04. По сути, при возврате локальной переменной возвращаемого типа она обычно обрабатывается как значение, поэтому по умолчанию разрешается перемещение.

Как я могу позволить оптимизации компилятора выполнять свою работу и по-прежнему применять перемещение, если RVO не применяется?

Как это:

HeavyWeight MakeHeavy()
{
    HeavyWeight heavy;
    return heavy;
}

Преобразование возврата в ход обязательно.

Другие вопросы по тегам