Создание объекта, ссылка на локальную переменную vs rvalue

Есть ли какое-либо преимущество в использовании ссылки на значение r при создании объекта, который в противном случае был бы в нормальной локальной переменной?

Foo&& cn = Foo();
cn.m_flag = 1;
bar.m_foo = std::move(cn);
//cn is not used again

Foo cn;
cn.m_flag = 1;
bar.m_foo = std::move(cn); //Is it ok to move a non rvalue reference?
//cn is not used again

В первом фрагменте кода кажется ясным, что копий не будет, но я бы предположил, что во втором компилятор будет оптимизировать копии?

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

Ответ 1

Эти фрагменты кода в основном эквивалентны. Это:

Foo&& rf = Foo();

связывает временную ссылку, которая продлевает время жизни временного ресурса до ссылки. Foo будет уничтожаться только тогда, когда rf выходит за рамки. С тем же самым поведением вы получаете:

Foo f;

за исключением того, что в последнем примере f инициализируется по умолчанию, но в предыдущем примере rf инициализируется значением. Для некоторых типов эти два эквивалентны. Для других это не так. Если бы вы написали Foo f{}, то это различие исчезнет.

Одно из оставшихся различий относится к копированию:

Foo give_a_foo_rv() {
    Foo&& rf = Foo();
    return rf;
}

Foo give_a_foo() {
    Foo f{};
    return f;
}

RVO не разрешается выполнять в первом примере, поскольку rf не имеет тот же тип, что и тип возврата give_a_foo_rv(). Более того, rf даже не будет автоматически перемещен в тип возвращаемого значения, потому что он не является объектом, поэтому он не имеет автоматической продолжительности хранения, поэтому дополнительная копия:

Foo f = give_a_foo_rv(); // a copy happens here!
Foo g = give_a_foo();    // no move or copy

кажется очевидным, что копий не будет.

Это полностью зависит от того, что делает движение Foo. Если Foo выглядит следующим образом:

struct Foo {
    Foo() = default;
    Foo(Foo const& ) = default;
    Foo& operator=(Foo const& ) = default;

    // some members
};

тогда перемещение a Foo все еще копирует.


И да, во втором примере это вполне нормально std::move(f). Вам не нужен объект типа rvalue для ссылки на T на move. Это серьезно ограничило бы полезность перемещения.