Я решил переписать этот ответ, так как Хорхе Перес любезно указал, что оригинал был ошибочным, и потому что ни один из других ответов на самом деле не касается оригинального вопроса.
Я начал с написания простого тестаПрограмма:
#include <iostream>
class Moveable
{
public:
Moveable () { std::cout << "Constructor\n"; }
~Moveable () { std::cout << "Destructor\n"; }
Moveable (const Moveable&) { std::cout << "Copy constructor\n"; }
Moveable& operator= (const Moveable&) { std::cout << "Copy assignment\n"; return *this; }
Moveable (const Moveable&&) { std::cout << "Move constructor\n"; }
Moveable& operator= (const Moveable&&) { std::cout << "Move assignment\n"; return *this; }
};
class A
{
public:
A (Moveable &&m) : m_m (std::move (m)) {}
Moveable m_m;
};
Moveable get_m ()
{
Moveable res;
return res;
}
int main ()
{
Moveable m = get_m ();
A (std::move (m));
}
, которая дает следующий вывод:
Constructor
Move constructor
Destructor
Destructor
Таким образом, вы сразу можете увидеть, что неэффективность в вашем коде не так плоха, как вы думаете - есть только один шаг ибез копий.
Теперь, как говорили другие:
Moveable m = get_m ();
ничего не копирует из-за Оптимизация именованного возвращаемого значения (NRVO) .
И есть только один ход, потому что:
A (std::move (m));
на самом деле ничего не двигает (это просто актерский состав).
Живая демоверсия
Что касается движения, то, возможно, яснее, что происходит, если вы измените это:
A (Moveable&& m) : m_m (std::move (m)) {}
на следующее:
A (Moveable& m) : m_m (std::move (m)) {}
, потому что вы можете изменить это:
A (std::move (m));
на это:
A {m};
и все равно получите тот же результатt (вам нужны скобки, чтобы избежать «самого неприятного разбора»).
Живая демоверсия