代码之家  ›  专栏  ›  技术社区  ›  Basilevs

什么是std::move(),什么时候应该使用它?

  •  505
  • Basilevs  · 技术社区  · 15 年前
    1. 这是怎么一回事?
    2. 它做什么?
    3. 什么时候使用?

    感谢良好的联系。

    6 回复  |  直到 6 年前
        1
  •  214
  •   einpoklum    6 年前

    Wikipedia Page on C++11 R-value references and move constructors

    1. 在C++ 11中,除了复制构造函数之外,对象还可以有移动构造函数。
      (除了复制分配运算符,它们还有移动分配运算符。)
    2. 如果对象具有类型“rvalue reference”,则使用move构造函数而不是copy构造函数。( Type && )
    3. std::move() 是一种转换,它生成对对象的右值引用,以允许从对象中移动。

    这是一种避免拷贝的新C++方法。例如,使用移动构造函数, std::vector 只能将指向数据的内部指针复制到新对象,使移动的对象处于错误状态,避免复制所有数据。这将是C++有效的。

    尝试谷歌搜索移动语义,右值,完美转发。

        2
  •  133
  •   einpoklum    6 年前

    1。这是什么?”

    同时 std::move() 从技术上讲是一种功能-我想说 它不是 真正地 函数 . 这是一种 转换器 在编译器考虑表达式值的方式之间。

    2。它是做什么的?”

    首先要注意的是 STD::MOVEL() 实际上什么都没动 . 它将表达式从 lvalue (如命名变量)为 xvalue . xvalue告诉编译器:

    你可以掠夺我, 移动 任何我持有并在其他地方使用的东西(因为我很快就会被销毁)”。

    换句话说,当你使用 std::move(x) ,您允许编译器将 x . 因此如果 X 比如说,在内存中有自己的缓冲区 STD::MOVEL() 编译程序可以让另一个对象拥有它。

    您也可以从 prvalue (比如你临时路过),但这很少有用。

    3。什么时候使用?”

    问这个问题的另一种方法是“我会为什么而蚕食现有对象的资源?”好吧,如果您正在编写应用程序代码,您可能不会在编译器创建的临时对象上浪费太多时间。因此,主要是在构造函数、运算符方法、标准库算法(如函数等)等对象自动创建和销毁的地方执行此操作。当然,这只是一个经验法则。

    典型的用法是将资源从一个对象“移动”到另一个对象,而不是复制。@Guillaume链接到 this page 它有一个简单的例子:交换两个对象而不需要复制。

    template <class T>
    swap(T& a, T& b) {
        T tmp(a);   // we now have two copies of a
        a = b;      // we now have two copies of b (+ discarded a copy of a)
        b = tmp;    // we now have two copies of tmp (+ discarded a copy of b)
    }
    

    使用“移动”可以交换资源,而不是复制它们:

    template <class T>
    swap(T& a, T& b) {
        T tmp(std::move(a));
        a = std::move(b);   
        b = std::move(tmp);
    }
    

    想想当t是,比如, vector<int> 大小为n。在第一个版本中,您读写3*n元素,在第二个版本中,您基本上只读写指向向量缓冲区的3个指针。当然,T类需要知道如何进行移动;您应该有一个移动赋值操作符和一个T类的移动构造函数,这样才能工作。

        3
  •  127
  •   Saurav Sahu    7 年前

    当需要在其他地方“传输”对象的内容时,可以使用move,而不需要进行复制(例如,内容不重复,这就是为什么可以在某些不可复制的对象上使用它,例如唯一的指针)。使用std::move,对象还可以在不进行复制(并节省大量时间)的情况下获取临时对象的内容。

    这个链接真的帮助了我:

    http://thbecker.net/articles/rvalue_references/section_01.html

    如果我的回答太晚了,我很抱歉,但我也在为std::move寻找一个好的链接,我发现上面的链接有点“严肃”。

    这将重点放在R值引用上,在这种情况下您应该使用它们,我认为它更详细,这就是为什么我想在这里共享这个链接。

        4
  •  40
  •   Christopher Oezbek    7 年前

    问:什么是 std::move ?

    答: std::move() 是C++标准库中的一个函数,用于将其转换为RValk引用。

    简单地 std::move(t) 相当于:

    static_cast<T&&>(t);
    

    右值是一个临时值,它不会在定义它的表达式之外持久存在,例如从未存储在变量中的中间函数结果。

    int a = 3; // 3 is a rvalue, does not exist after expression is evaluated
    int b = a; // a is a lvalue, keeps existing after expression is evaluated
    

    std::move()的实现在 N2027: "A Brief Introduction to Rvalue References" 如下:

    template <class T>
    typename remove_reference<T>::type&&
    std::move(T&& a)
    {
        return a;
    }
    

    正如你所看到的, 性病:移动 收益率 T&& 无论是否用值调用( T )参考类型( T& )或右值引用( T&&&; )

    问:它是做什么的?

    答:作为一个演员,它在运行时不做任何事情。只有在编译时告诉编译器您希望继续将引用视为右值才是相关的。

    foo(3 * 5); // obviously, you are calling foo with a temporary (rvalue)
    
    int a = 3 * 5;
    foo(a);     // how to tell the compiler to treat `a` as an rvalue?
    foo(std::move(a)); // will call `foo(int&& a)` rather than `foo(int a)` or `foo(int& a)`
    

    它做什么 做:

    • 复制参数
    • 调用复制构造函数
    • 更改参数对象

    问:什么时候使用?

    A:你应该用 性病:移动 如果要调用支持移动语义的函数,并使用一个不是右值(临时表达式)的参数。

    这为我提出了以下后续问题:

    • 什么是移动语义?与复制语义相反,移动语义是一种编程技术,其中对象的成员通过“接管”而不是复制另一个对象的成员来初始化。这种“接管”只对指针和资源句柄有意义,通过复制指针或整型句柄(而不是基础数据)可以很便宜地进行转移。

    • 什么类和对象支持移动语义?作为一个开发人员,您可以在自己的类中实现移动语义,前提是这些类可以从传输成员而不是复制成员中获益。一旦实现了移动语义,您将直接受益于许多库程序员的工作,他们为高效地处理具有移动语义的类添加了支持。

    • 为什么编译器不能自己解决呢?编译器不能调用函数的另一个重载,除非您这样说。必须帮助编译器选择是否应调用函数的常规版本或移动版本。

    • 在哪些情况下,我想告诉编译器它应该将变量视为右值?这很可能发生在模板或库函数中,您知道中间结果可以被挽救。

        5
  •  28
  •   Christopher Oezbek    8 年前

    移动本身并没有多大作用。我认为它调用了对象的移动构造函数,但实际上它只是执行类型转换(将左值变量强制转换为右值,以便将所述变量作为参数传递给移动构造函数或赋值运算符)。

    所以std::move只是作为使用move语义的前兆。移动语义本质上是处理临时对象的有效方法。

    考虑对象 A = B + C + D + E + F;

    这是好看的代码,但E+F生成一个临时对象。然后d+temp生成另一个临时对象,依此类推。在类的每个普通“+”运算符中,都会出现深度复制。

    例如

    Object Object::operator+ (const Object& rhs) {
        Object temp (*this);
        // logic for adding
        return temp;
    }
    

    在这个函数中创建临时对象是无用的-这些临时对象一旦超出范围,将在行尾被删除。

    我们更愿意使用移动语义来“掠夺”临时对象,并做类似的事情

     Object& Object::operator+ (Object&& rhs) {
         // logic to modify rhs directly
         return rhs;
     }
    

    这样就避免了不必要的深度复制。关于这个例子,唯一发生深度复制的部分现在是e+f,其余部分使用移动语义。还需要实现move构造函数或赋值运算符来将结果赋给。

        6
  •  2
  •   Jayhello    6 年前

    What is it? What does it do? 以上已作说明。

    我举个例子 when it should be used.

    例如,我们有一个类,其中包含大量资源,比如大数组。

    class ResHeavy{ //  ResHeavy means heavy resource
        public:
            ResHeavy(int len=10):_upInt(new int[len]),_len(len){
                cout<<"default ctor"<<endl;
            }
    
            ResHeavy(const ResHeavy& rhs):_upInt(new int[rhs._len]),_len(rhs._len){
                cout<<"copy ctor"<<endl;
            }
    
            ResHeavy& operator=(const ResHeavy& rhs){
                _upInt.reset(new int[rhs._len]);
                _len = rhs._len;
                cout<<"operator= ctor"<<endl;
            }
    
            ResHeavy(ResHeavy&& rhs){
                _upInt = std::move(rhs._upInt);
                _len = rhs._len;
                rhs._len = 0;
                cout<<"move ctor"<<endl;
            }
    
        // check array valid
        bool is_up_valid(){
            return _upInt != nullptr;
        }
    
        private:
            std::unique_ptr<int[]> _upInt; // heavy array resource
            int _len; // length of int array
    };
    

    测试代码:

    void test_std_move2(){
        ResHeavy rh; // only one int[]
        // operator rh
    
        // after some operator of rh, it becomes no-use
        // transform it to other object
        ResHeavy rh2 = std::move(rh); // rh becomes invalid
    
        // show rh, rh2 it valid
        if(rh.is_up_valid())
            cout<<"rh valid"<<endl;
        else
            cout<<"rh invalid"<<endl;
    
        if(rh2.is_up_valid())
            cout<<"rh2 valid"<<endl;
        else
            cout<<"rh2 invalid"<<endl;
    
        // new ResHeavy object, created by copy ctor
        ResHeavy rh3(rh2);  // two copy of int[]
    
        if(rh3.is_up_valid())
            cout<<"rh3 valid"<<endl;
        else
            cout<<"rh3 invalid"<<endl;
    }
    

    输出如下:

    default ctor
    move ctor
    rh invalid
    rh2 valid
    copy ctor
    rh3 valid
    

    我们可以看到 std::move 具有 move constructor 使转换资源变得容易。

    std::move还有什么用处?

    std::move在对元素数组排序时也很有用。许多排序算法(如选择排序和气泡排序)通过交换成对的元素来工作。在之前,我们必须使用复制语义来进行交换。现在我们可以使用移动语义,这是更有效的。

    如果我们想将由一个智能指针管理的内容移到另一个智能指针管理的内容,它也很有用。

    被引者

    https://www.learncpp.com/cpp-tutorial/15-4-stdmove/