когда использовать move в вызовах функций

В настоящее время я изучаю все возможности С++ 11/14 и задаюсь вопросом, когда использовать std :: move в вызовах функций.

Я знаю, что не должен использовать его при возврате локальных переменных, потому что это нарушает оптимизацию возвращаемого значения, но я не совсем понимаю, где в вызовах функций приведение к значению действительно помогает.

Ответы

Ответ 1

Когда функция принимает ссылку на rvalue, вы должны предоставить значение rvalue (либо уже имея значение prvalue, либо используя std::move для создания значения xvalue). Например

void foo(std::string&& s);

std::string s;

foo(s);            // Compile-time error
foo(std::move(s)); // OK
foo(std::string{}) // OK

Когда функция принимает значение, вы можете использовать std::move для перемещения аргумента функции вместо конструирования копии. Например

void bar(std::string s);

std::string s;

bar(s);             // Copies into 's'
bar(std::move(s));  // Moves into 's'

Когда функция принимает ссылку для пересылки, вы можете использовать std::move чтобы функция могла перемещать объект дальше по стеку вызовов. Например

template <typename T>
void pipe(T&& x)
{
    sink(std::forward<T>(x));
}

std::string s;

pipe(s);             // 'std::forward' will do nothing
pipe(std::move(s));  // 'std::forward' will move
pipe(std::string{}); // 'std::forward' will move

Ответ 2

Если у вас есть какой - то существенный объект, и вы передаете его в качестве аргумента в функцию (например, API, или контейнер emplace операцию), и вы больше не нужно на callsite, так что вы хотите передать в собственность, а чем копировать то "сразу" теряя оригинал. Это когда вы двигаете это.

void StoreThing(std::vector<int> v);

int main()
{
    std::vector<int> v{1,2,3,4,5,6/*,.....*/};
    StoreThing(v);
}

// Copies 'v' then lets it go out of scope. Pointless!

против:

void StoreThing(std::vector<int> v);

int main()
{
    std::vector<int> v{1,2,3,4,5,6/*,.....*/};
    StoreThing(std::move(v));
}

// Better! We didn't need 'v' in 'main' any more...

Это происходит автоматически при возврате локальных переменных, если RVO не был применен (и обратите внимание, что такая "оптимизация" обязательна начиная с С++ 17, так что вы правы сказать, что добавление "избыточного" std::move в этом дело может на самом деле быть вредным).

Также std::move не имеет смысла, если вы передаете что-то действительно маленькое (особенно не относящееся к классу, которое не может иметь конструктор перемещения, не говоря уже о значимом конструкторе!) Или вы знаете, что переходите в функцию, которая принимает его аргументы const -ly; в этом случае вам решать, хотите ли вы сохранить добавленный исходный код, отвлекаясь от std::move, который ничего не сделает: на первый взгляд, но в шаблоне вы не уверены.