是什么? 它能做什么? 什么时候使用?

好的链接是赞赏的。


当前回答

下面是一个完整的例子,使用std::move来处理一个(简单的)自定义向量

预期的输出:

 c: [10][11]
 copy ctor called
 copy of c: [10][11]
 move ctor called
 moved c: [10][11]

编译:

  g++ -std=c++2a -O2 -Wall -pedantic foo.cpp

代码:

#include <iostream>
#include <algorithm>

template<class T> class MyVector {
private:
    T *data;
    size_t maxlen;
    size_t currlen;
public:
    MyVector<T> () : data (nullptr), maxlen(0), currlen(0) { }
    MyVector<T> (int maxlen) : data (new T [maxlen]), maxlen(maxlen), currlen(0) { }

    MyVector<T> (const MyVector& o) {
        std::cout << "copy ctor called" << std::endl;
        data = new T [o.maxlen];
        maxlen = o.maxlen;
        currlen = o.currlen;
        std::copy(o.data, o.data + o.maxlen, data);
    }

    MyVector<T> (const MyVector<T>&& o) {
        std::cout << "move ctor called" << std::endl;
        data = o.data;
        maxlen = o.maxlen;
        currlen = o.currlen;
    }

    void push_back (const T& i) {
        if (currlen >= maxlen) {
            maxlen *= 2;
            auto newdata = new T [maxlen];
            std::copy(data, data + currlen, newdata);
            if (data) {
                delete[] data;
            }
            data = newdata;
        }
        data[currlen++] = i;
    }

    friend std::ostream& operator<<(std::ostream &os, const MyVector<T>& o) {
        auto s = o.data;
        auto e = o.data + o.currlen;;
        while (s < e) {
            os << "[" << *s << "]";
            s++;
        }
        return os;
    }
};

int main() {
    auto c = new MyVector<int>(1);
    c->push_back(10);
    c->push_back(11);
    std::cout << "c: " << *c << std::endl;
    auto d = *c;
    std::cout << "copy of c: " << d << std::endl;
    auto e = std::move(*c);
    delete c;
    std::cout << "moved c: " << e << std::endl;
}

其他回答

下面是一个完整的例子,使用std::move来处理一个(简单的)自定义向量

预期的输出:

 c: [10][11]
 copy ctor called
 copy of c: [10][11]
 move ctor called
 moved c: [10][11]

编译:

  g++ -std=c++2a -O2 -Wall -pedantic foo.cpp

代码:

#include <iostream>
#include <algorithm>

template<class T> class MyVector {
private:
    T *data;
    size_t maxlen;
    size_t currlen;
public:
    MyVector<T> () : data (nullptr), maxlen(0), currlen(0) { }
    MyVector<T> (int maxlen) : data (new T [maxlen]), maxlen(maxlen), currlen(0) { }

    MyVector<T> (const MyVector& o) {
        std::cout << "copy ctor called" << std::endl;
        data = new T [o.maxlen];
        maxlen = o.maxlen;
        currlen = o.currlen;
        std::copy(o.data, o.data + o.maxlen, data);
    }

    MyVector<T> (const MyVector<T>&& o) {
        std::cout << "move ctor called" << std::endl;
        data = o.data;
        maxlen = o.maxlen;
        currlen = o.currlen;
    }

    void push_back (const T& i) {
        if (currlen >= maxlen) {
            maxlen *= 2;
            auto newdata = new T [maxlen];
            std::copy(data, data + currlen, newdata);
            if (data) {
                delete[] data;
            }
            data = newdata;
        }
        data[currlen++] = i;
    }

    friend std::ostream& operator<<(std::ostream &os, const MyVector<T>& o) {
        auto s = o.data;
        auto e = o.data + o.currlen;;
        while (s < e) {
            os << "[" << *s << "]";
            s++;
        }
        return os;
    }
};

int main() {
    auto c = new MyVector<int>(1);
    c->push_back(10);
    c->push_back(11);
    std::cout << "c: " << *c << std::endl;
    auto d = *c;
    std::cout << "copy of c: " << d << std::endl;
    auto e = std::move(*c);
    delete c;
    std::cout << "moved c: " << e << std::endl;
}

c++ 11 R-value引用和move构造函数的维基百科页面

在c++ 11中,除了复制构造函数,对象还可以有移动构造函数。 (除了复制赋值操作符,它们还有移动赋值操作符。) 如果对象的类型为“rvalue-reference”(type &&),则使用移动构造函数而不是复制构造函数。 move()是一个强制转换,它产生一个指向对象的右值引用,以实现从对象中移动。

这是一种新的c++避免复制的方法。例如,使用move构造函数,std::vector可以将其指向数据的内部指针复制到新对象,使被移动的对象处于moved from状态,因此不复制所有数据。这将是c++有效的。

试试谷歌一下移动语义,右值,完美转发。

Std::move本身除了static_cast之外什么也不做。根据cppreference.com网站

它完全等价于static_cast到右值引用类型。

因此,它取决于你在移动后赋值给的变量的类型,如果该类型具有接受右值形参的构造函数或赋值操作符,它可能会或可能不会窃取原始变量的内容,因此,它可能会让原始变量处于未指定的状态:

除非另有说明,否则所有移出的标准库对象都处于有效但未指定的状态。

因为对于内置文字类型(如整数和原始指针)没有特殊的移动构造函数或移动赋值操作符,因此,它将只是对这些类型进行简单的复制。

当你需要将一个对象的内容“转移”到其他地方时,你可以使用move,而不需要进行复制(即内容不复制,这就是为什么它可以用于一些不可复制的对象,如unique_ptr)。使用std::move,对象也可以在不复制的情况下获取临时对象的内容(节省大量时间)。

这个链接真的帮了我大忙:

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

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

这强调了r值引用,在什么情况下你应该使用它们,我认为它更详细,这就是为什么我想在这里分享这个链接。

1. “这是什么?”

虽然std::move()技术上是一个函数-我想说它不是一个真正的函数。它有点像编译器考虑表达式值的方式之间的转换器。

2. “它能做什么?”

首先要注意的是std::move()实际上并不移动任何东西。它将表达式从左值(如命名变量)更改为xvalue。xvalue告诉编译器:

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

换句话说,当你使用std::move(x)时,你是在允许编译器蚕食x。因此,如果x在内存中有自己的缓冲区-在std::move()ing之后,编译器可以让另一个对象拥有它。

您还可以从一个prvalue(例如传递的临时值)中移动,但这很少有用。

3.“什么时候使用?”

问这个问题的另一种方法是“我要为了什么而占用现有对象的资源?”好吧,如果您正在编写应用程序代码,那么您可能不会过多地使用编译器创建的临时对象。主要是在构造函数,操作符方法,标准库算法类函数等地方,在这些地方,对象会自动创建和销毁。当然,这只是一个经验法则。

典型的用法是将资源从一个对象“移动”到另一个对象,而不是复制。@Guillaume链接到这个页面,上面有一个简单的例子:用更少的复制交换两个对象。

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)
}

使用move可以让你交换资源,而不是复制它们:

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

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