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

好的链接是赞赏的。


当前回答

问:什么是std::move?

答:std::move()是c++标准库中用于强制转换为右值引用的函数。

简单来说,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中给出:“右值引用简介”如下所示:

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

如你所见,std::move返回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)`

它没有做什么:

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

问:什么时候使用?

答:如果你想调用一个参数不是右值(临时表达式)的支持move语义的函数,应该使用std::move。

这就引出了以下问题:

What is move semantics? Move semantics in contrast to copy semantics is a programming technique in which the members of an object are initialized by 'taking over' instead of copying another object's members. Such 'take over' makes only sense with pointers and resource handles, which can be cheaply transferred by copying the pointer or integer handle rather than the underlying data. What kind of classes and objects support move semantics? It is up to you as a developer to implement move semantics in your own classes if these would benefit from transferring their members instead of copying them. Once you implement move semantics, you will directly benefit from work from many library programmers who have added support for handling classes with move semantics efficiently. Why can't the compiler figure it out on its own? The compiler cannot just call another overload of a function unless you say so. You must help the compiler choose whether the regular or move version of the function should be called. In which situations would I want to tell the compiler that it should treat a variable as an rvalue? This will most likely happen in template or library functions, where you know that an intermediate result could be salvaged (rather than allocating a new instance).

其他回答

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

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

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

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

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

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

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

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

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

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

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

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

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

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

问:什么是std::move?

答:std::move()是c++标准库中用于强制转换为右值引用的函数。

简单来说,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中给出:“右值引用简介”如下所示:

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

如你所见,std::move返回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)`

它没有做什么:

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

问:什么时候使用?

答:如果你想调用一个参数不是右值(临时表达式)的支持move语义的函数,应该使用std::move。

这就引出了以下问题:

What is move semantics? Move semantics in contrast to copy semantics is a programming technique in which the members of an object are initialized by 'taking over' instead of copying another object's members. Such 'take over' makes only sense with pointers and resource handles, which can be cheaply transferred by copying the pointer or integer handle rather than the underlying data. What kind of classes and objects support move semantics? It is up to you as a developer to implement move semantics in your own classes if these would benefit from transferring their members instead of copying them. Once you implement move semantics, you will directly benefit from work from many library programmers who have added support for handling classes with move semantics efficiently. Why can't the compiler figure it out on its own? The compiler cannot just call another overload of a function unless you say so. You must help the compiler choose whether the regular or move version of the function should be called. In which situations would I want to tell the compiler that it should treat a variable as an rvalue? This will most likely happen in template or library functions, where you know that an intermediate result could be salvaged (rather than allocating a new instance).

“它是什么?”和“它做什么?”已经在上面解释过了。

我将给出一个“什么时候应该使用它”的例子。

例如,我们有一个类,里面有很多资源,比如大数组。

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

我们可以看到带有move构造函数的std::move使得转换资源变得很容易。

还有什么地方是std::move有用?

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

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

引用:

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