когда использовать 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
, который ничего не сделает: на первый взгляд, но в шаблоне вы не уверены.