• c++ std::move和std::forward总结与使用


    右值和左值的区别:

    当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。左值与右值的根本区别在于是否允许取地址&运算符获得对应的内存地址。
    C++/C++11中左值、左值引用、右值、右值引用的使用

    std::move解析

    std::move的唯一功能就是将一个左值引用强制转化为右值引用

    	std::string str = "hello";
        std::vector<std::string> v;
        //调用拷贝构造函数
        v.push_back(str);
        //调用移动构造函数,掏空str,掏空后,不可以再使用str
        v.push_back(std::move(str));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    std::move源码解析

    std::move的函数原型定义:

    template <typename T>
    typename remove_reference<T>::type&& move(T&& t)
    {
    	return static_cast<typename remove_reference<T>::type &&>(t);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    首先函数参数T&&是一个指向T类型的右值引用,通过引用折叠,将T&&传递过来的类型,右值保持不变,左值变成普通的左值引用,然后在通过remove_reference::type模板移除T&&,T&的引用,获取到具体的类型T

    //原始的,最通用的版本
    template <typename T> struct remove_reference{
        typedef T type;  //定义T的类型别名为type
    };
     
    //部分版本特例化,将用于左值引用和右值引用
    template <class T> struct remove_reference<T&> //左值引用
    { typedef T type; }
     
    template <class T> struct remove_reference<T&&> //右值引用
    { typedef T type; }   
      
    //举例如下,下列定义的a、b、c三个变量都是int类型
    int i;
    remove_refrence<decltype(42)>::type a;             //使用原版本,
    remove_refrence<decltype(i)>::type  b;             //左值引用特例版本
    remove_refrence<decltype(std::move(i))>::type  b;  //右值引用特例版本 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    引用折叠,其实就是多个引用的意思,所有的引用折叠最终都代表一个引用,要么是左值引用,要么是右值引用
    规则就是:
    如果任一引用为左值引用,则结果为左值引用,否则为右值引用
    比如 int& &&等价于int &
    这个的应用场景主要为函数模板的推导
    https://zhuanlan.zhihu.com/p/50816420

    std::forward解析

    在任何的函数内部,对形参的直接使用,都是按照左值进行的,比如:

    template<typename T>
    void func(T& t){
       std::cout<<"func(T& t)"<<std::endl;
    }
    
    template<typename T>
    void func(T&& t){
        std::cout<<"func(T&& t)"<<std::endl;
    }
    
    template<typename T>
    void printType(T&& t){
        func(t);
    }
    int main()
    {
        int  i = 0;
        printType(i);
        printType(2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这段代码的打印为:

    func(T& t)
    func(T& t)
    
    • 1
    • 2

    那我们要怎么让调用的函数按我们的预期执行呢,答案就是使用std::forward:

    template<typename T>
    void printType(T&& t){
        func(std::forward<T>(t));
    }
    
    • 1
    • 2
    • 3
    • 4

    打印为:

    func(T& t)
    func(T&& t)
    
    • 1
    • 2

    std::forward源码解析

    template <class _Tp>
    _Tp&&
    forward(typename remove_reference<_Tp>::type& __t) _NOEXCEPT
    {
        return static_cast<_Tp&&>(__t);
    }
    
    template <class _Tp>
    _Tp&&
    forward(typename remove_reference<_Tp>::type&& __t) _NOEXCEPT
    {
        return static_cast<_Tp&&>(__t);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    首先通过remove_reference拿到_Tp的直接类型,如int类型

    • 对于第一个函数,通过remove_reference,t的类型是int&,再通过static_cast中的引用折叠,static_cast<_Tp& &&>,最终拿到的是左值引用
    • 对于第二个函数,通过remove_reference,t的类型是int&&,再通过static_cast中的引用折叠,static_cast<_Tp&& &&>,最终拿到的是右值引用

    因此这样实现了完美转发

    参考文档:
    https://blog.csdn.net/daaikuaichuan/article/details/88371948

  • 相关阅读:
    windows环境配置
    Ajax实现搜索联想 自动补全
    线性表操作集锦(顺序表,链表,栈,队列)
    分布式事务之Seata AT
    【Codecs系列】H.264码率控制算法之URQ模型
    Harmony设计模式-单例模式
    ExtJS - ExtJS实例
    JS如何实现一个注册按钮10秒倒计时效果
    简历准备及面试技巧,你应该知道的一切
    【1582. 二进制矩阵中的特殊位置】
  • 原文地址:https://blog.csdn.net/qq_36391075/article/details/125678790