std::move

https://en.cppreference.com/w/cpp/utility/move

std::move 获得右值引用

Defined in header <utility>

template< class T >
typename std::remove_reference<T>::type&& move( T&& t ) noexcept;(since C++11)(until C++14)                                                     |

template< class T >
constexpr std::remove_reference_t<T>&& move( T&& t ) noexcept;(since C++14) 

std::move用作指明一个对象t可以被移动,允许移动有效的资源从t到另一个对象

特别是,std::move产生了一个xvalue(亡值)表达式来定义它的实参t。这与static_cast转化一个rvalue(右值)引用类型是一样的

参数

t - 要移动的对象

返回值

static_cast<typename std::remove_reference<T>::type&&>(t) 

注意

在传递一个rvalue(右值)实参的时候(包括prvalue(纯右值)比如临时对象和xvalues(亡值)比如通过std::move产生的),通过重载策略,接收rvalue(右值)引用参数的函数(包括move构造器,move移动操作符和常规函数比如std::vector::push_back)会被选则。如果实参定义了一个拥有资源对象,重载有这个选项,但是并不要求,就是移动任何这个实参拥有的资源。比如链表的移动构造器,会拷贝投结点,并且保存nullptr指针,而不是拷贝每一个结点。

rvalue(引用)变量的名称是lvalues(左值),这一点需要注意,所以必须要在需要rvalue(右值)引用形参的地方转换为xvalue(亡值)来绑定到函数的重载上,这也就是为什么移动构造函数和移动赋值函数都需要调用std::move

// Simple move constructor
A(A&& arg) : member(std::move(arg.member)) // the expression "arg.member" is lvalue
{} 
// Simple move assignment operator
A& operator=(A&& other) {
     member = std::move(other.member);
     return *this;
}

有一个例外,当函数的参数是类型模板参数,并且是rvalue(右值)引用(转发引用或者通用引用),这是应该使用std::forward

除了其他特殊情况,所有标准的类库对象都在被移动后存放在合法但是未定义的状态。也就是,只有函数没有先决条件,比如赋值操作,可以安全的使用这个对象,在移动后

std::vector<std::string> v;
std::string str = "example";
v.push_back(std::move(str)); // str is now valid but unspecified
str.back(); // undefined behavior if size() == 0: back() has a precondition !empty() 这里有问题,因为str的内容已经被移给了v
str.clear(); // OK, clear() has no preconditions

同样,标准函数调用通过xvalue(亡值)可以认为指向这个对象的引用;如果通过lvalue(左值)的std::move构建,没有别名检查。不管怎样,标准库的自动移动赋值会保证对象处在合法未定义的状态。

std::vector<int> v = {2, 3, 3};
v = std::move(v); // the value of v is unspecified v是合法的,但是未定义

示例

#include <iostream>
#include <utility>
#include <vector>
#include <string>
 
int main()
{
    std::string str = "Hello";
    std::vector<std::string> v;
 
    // 使用 push_back(const T&) 重载,
    // 表示我们将带来复制 str 的成本
    v.push_back(str);
    std::cout << "After copy, str is "" << str << ""
";
 
    // 使用右值引用 push_back(T&&) 重载,
    // 表示不复制字符串;而是
    // str 的内容被移动进 vector
    // 这个开销比较低,但也意味着 str 现在可能为空。
    v.push_back(std::move(str));
    std::cout << "After move, str is "" << str << ""
";
 
    std::cout << "The contents of the vector are "" << v[0]
                                         << "", "" << v[1] << ""
";
}

可能的输出

After copy, str is "Hello"
After move, str is ""
The contents of the vector are "Hello", "Hello"

std::move就是把一个左值转换为右值,为什么有时候会有数据被剪切的假象呢,这就是类,比如std::string又重载了右值引用的拷贝或者构造函数,在里面做了手脚,它认为右值是临时变量,反正立马就要销毁,不如直接把已经申请的内存拿来用,减少不必要的拷贝,所以用str::move传递一个左值时,出来后数据被移动的原因。参考下面的例子

class mstring
{
public:
    mstring()
    {
        m_v = NULL;
    }
    mstring(mstring&& a)
    {
        //如果没有这两步,a的数据是不会传递给b的,没有任何效果
        m_v = a.m_v;
        a.m_v = NULL;
    }
    char* m_v;
};

int main()
{
    mstring a;
    a.m_v = "abc";
    mstring b(std::move(a));
    return 0;
}

版权声明:本文版权归作者所有,如需转载,请标明出处

原文地址:https://www.cnblogs.com/studywithallofyou/p/14576575.html