zoukankan      html  css  js  c++  java
  • 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;
    }
    

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

  • 相关阅读:
    Java.util.Timer的灵活配置与使用
    js字符变量判空
    js获取当前时间格式化字符串
    Java快速获取格式化的日期字符串
    img标签显示图片方法总结
    Java获取给定日期的月初和月末两个日期
    SpringBoot学习--07配置Druid数据库连接池
    SpringBoot学习--06使用jackson返回json数据
    SpringBoot学习--05SpringBoot整合Mybatis(下)(mybatis中踩过的坑)--不定时更新
    SpringBoot学习--04SpringBoot整合Mybatis(上)(配置mybatis generator)
  • 原文地址:https://www.cnblogs.com/studywithallofyou/p/14576575.html
Copyright © 2011-2022 走看看