ES.56: Write std::move() only when you need to explicitly move an object to another scope



We move, rather than copy, to avoid duplication and for improved performance.


A move typically leaves behind an empty object (C.64), which can be surprising or even dangerous, so we try to avoid moving from lvalues (they might be accessed later).



Moving is done implicitly when the source is an rvalue (e.g., value in a return treatment or a function result), so don't pointlessly complicate code in those cases by writing move explicitly. Instead, write short functions that return values, and both the function's return and the caller's accepting of the return will be optimized naturally.


In general, following the guidelines in this document (including not making variables' scopes needlessly large, writing short functions that return values, returning local variables) help eliminate most need for explicit std::move.


Explicit move is needed to explicitly move an object to another scope, notably to pass it to a "sink" function and in the implementations of the move operations themselves (move constructor, move assignment operator) and swap operations.




Example, bad(反面示例)

void sink(X&& x);   // sink takes ownership of x

void user()
    X x;
    // error: cannot bind an lvalue to a rvalue reference
    // OK: sink takes the contents of x, x must now be assumed to be empty

    // ...

    // probably a mistake

Usually, a std::move() is used as an argument to a && parameter. And after you do that, assume the object has been moved from (see C.64) and don't read its state again until you first set it to a new value.


void f() {
    string s1 = "supercalifragilisticexpialidocious";

    string s2 = s1;             // ok, takes a copy
    assert(s1 == "supercalifragilisticexpialidocious");  // ok

    // bad, if you want to keep using s1's value
    string s3 = move(s1);

    // bad, assert will likely fail, s1 likely changed
    assert(s1 == "supercalifragilisticexpialidocious");
void sink(unique_ptr<widget> p);  // pass ownership of p to sink()

void f() {
    auto w = make_unique<widget>();
    // ...
    sink(std::move(w));               // ok, give to sink()
    // ...
    sink(w);    // Error: unique_ptr is carefully designed so that you cannot copy it

std::move() is a cast to && in disguise; it doesn't itself move anything, but marks a named object as a candidate that can be moved from. The language already knows the common cases where objects can be moved from, especially when returning values from functions, so don't complicate code with redundant std::move()'s.


Never write std::move() just because you've heard "it's more efficient." In general, don't believe claims of "efficiency" without data (???). In general, don't complicate your code without reason (??). Never write std::move() on a const object, it is silently transformed into a copy (see Item 23 in Meyers15)


Example, bad(反面示例)

vector<int> make_vector() {
    vector<int> result;
    // ... load result with data
    return std::move(result);       // bad; just write "return result;"

Never write return move(local_variable);, because the language already knows the variable is a move candidate. Writing move in this code won't help, and can actually be detrimental because on some compilers it interferes with RVO (the return value optimization) by creating an additional reference alias to the local variable.


Example, bad(反面示例)

vector<int> v = std::move(make_vector());   // bad; the std::move is entirely redundant

Never write move on a returned value such as x = move(f()); where f returns by value. The language already knows that a returned value is a temporary object that can be moved from.



void mover(X&& x) {
    call_something(std::move(x));         // ok
    call_something(std::forward<X>(x));   // bad, don't std::forward an rvalue reference
    call_something(x);                    // suspicious, why not std::move?

template<class T>
void forwarder(T&& t) {
    call_something(std::move(t));         // bad, don't std::move a forwarding reference
    call_something(std::forward<T>(t));   // ok
    call_something(t);                    // suspicious, why not std::forward?
  • Flag use of std::move(x) where x is an rvalue or the language will already treat it as an rvalue, including return std::move(local_variable); and std::move(f()) on a function that returns by value.
  • 标记针对右值或者已经被语言看作是右值的对象调用std::move的情况。包括std::move(local_variable);,std::move(f()),这里函数f是一个以传值方式返回结果的函数。
  • Flag functions taking an S&& parameter if there is no const S& overload to take care of lvalues.
  • 标记没有用于处理左值的const S&型重载函数,只有一个处理右值(参数类型:S&&)的函数的情况。
  • Flag a std::moves argument passed to a parameter, except when the parameter type is an X&& rvalue reference or the type is move-only and the parameter is passed by value.
  • 标记向参数传递std::move执行结果的情况,除非参数类型是右值引用类型X&&,或者参数类型为只移动不拷贝类型并且以传值方式传递。
  • Flag when std::move is applied to a forwarding reference (T&& where T is a template parameter type). Use std::forward instead.
  • 标记对转交引用类型调用std::move的情况(T&&,这里T是模板参数)。
  • Flag when std::move is applied to other than an rvalue reference to non-const. (More general case of the previous rule to cover the non-forwarding cases.)
  • 标记std::move运用于指向非常变量的右值引用以外的情况。(前面规则的更普遍形式,以包含非转交参数的情况)
  • Flag when std::forward is applied to an rvalue reference (X&& where X is a concrete type). Use std::move instead.
  • 标记std::forward用于右值引用的情况(X&&,这里X是具体类型),转而使用std::move。
  • Flag when std::forward is applied to other than a forwarding reference. (More general case of the previous rule to cover the non-moving cases.)
  • 标记std::forward用于转交引用之外的情况。(前面规则的更普遍形式,它可以覆盖非移动参数的情况。)
  • Flag when an object is potentially moved from and the next operation is a const operation; there should first be an intervening non-const operation, ideally assignment, to first reset the object's value.
  • 标记对象可能被执行移出操作而且下一个是常量操作(读取对象值,译者注)的情况;哪里应该首先有一个非常量操作(以便修改对象值,译者注),最好是重新设置对象值的赋值操作。



本文分享自微信公众号 - 面向对象思考(OOThinkingDalian),作者:面向对象思考

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。




0 条评论
登录 后参与评论


  • C++核心准则CP.61:使用async启动并发任务

    Similar to R.12, which tells you to avoid raw owning pointers, you should also a...

  • C++核心准则编译边学-F.18 使用X&&传递“将会发生数据移动”的参数并实施数据移动

    It's efficient and eliminates bugs at the call site: X&& binds to rvalues, which...

  • C++核心准则R.3: 原始指针(T*)不应拥有所有权

    There is nothing (in the C++ standard or in most code) to say otherwise and most...

  • C++之IO格式控制

    C语言中,我们可以通过函数printf和scanf进行格式化控制,而在C++中仍然包含了前者,但还提供了以下两种格式控制的方法: (1)使用流成员函数进行格...

  • [译]C++17,optional, any, 和 variant 的更多细节

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tkokof1/article/details/826...

  • C++ IO格式控制

    C语言中,我们可以通过函数printf和scanf进行格式化控制,而在C++中仍然包含了前者,但还提供了以下两种格式控制的方法: (1)使用流成员函数进行格式...

  • 简单的C++11线程池实现

    线程池的C++11简单实现,源代码来自Github上作者progschj,地址为:A simple C++11 Thread Pool implementati...

  • C++11特性


  • C++核心准则CP.61:使用async启动并发任务

    Similar to R.12, which tells you to avoid raw owning pointers, you should also a...

  • c++11多线程笔记

    1 thread类 thread f; 线程等待join() 线程分离detach()