• c++右值引用


    右值

    右值是相对与左值来说的。

    左值是以变量的形式存在,指向一个指定的内存,可以对它取地址。右值就是不指向任何地方,它是暂时和短命的,不能对它取地址。

    右值引用

    把临时的、生命周期短的值,绑定到一个变量上,提高它的生命周期,比如

        string a = "hello ";
        string b = "world";
    
        string c = a + b; // 1.临时变量赋值给c后自动释放
    
        string&& d = a + b; // 2.d 直接指向了临时变量,避免了一次赋值运算

    这里的 a+b 会产生一个临时变量,第一种情况,这个临时变量被拷贝构造给了c,之后,这个临时变量析构掉; 第二种情况,这个临时变量被右值引用d接管,它就不会被析构,而是一直存在,直到d退出作用域。

    因为右值引用能保存临时变量,所以在有些时候,它可以提高程序的效率,减少不必要的拷贝。

    当函数既有左值引用重载,又有右值引用重载的时候,左值引用绑定左值,右值引用绑定右值,如下:

    #include <iostream>
    #include <utility>
     
    void f(int& x) {
        std::cout << "lvalue reference overload f(" << x << ")
    ";
    }
     
    void f(const int& x) {
        std::cout << "lvalue reference to const overload f(" << x << ")
    ";
    }
     
    void f(int&& x) {
        std::cout << "rvalue reference overload f(" << x << ")
    ";
    }
     
    int main() {
        int i = 1;
        const int ci = 2;
        f(i);  // calls f(int&)
        f(ci); // calls f(const int&)
        f(3);  // calls f(int&&)
               // would call f(const int&) if f(int&&) overload wasn't provided
        f(std::move(i)); // calls f(int&&)
     
        // rvalue reference variables are lvalues when used in expressions
        int&& x = 1;
        f(x);            // calls f(int& x)
        f(std::move(x)); // calls f(int&& x)
    }

    这是move的基础,移动构造,移动赋值的基础。也可以将一个不再需要的实体移除作用域:

    std::vector<int> v{1,2,3,4,5};
    std::vector<int> v2(std::move(v)); // binds an rvalue reference to v
    assert(v.empty());

    左值引用

    引用需要被初始化到一个实体或函数。引用本身不是一个实体,它只是一个别名,所以,没有引用数组、指向引用的指针、指向引用的引用

    int& a[3]; // error
    int&* p;   // error
    int& &r;   // error

    引用折叠

    当使用typedef或者template时候,可能会导致引用折叠:只有两个都是右值引用的时候,才得到右值引用

    typedef int&  lref;
    typedef int&& rref;
    int n;
    lref&  r1 = n; // type of r1 is int&
    lref&& r2 = n; // type of r2 is int&
    rref&  r3 = n; // type of r3 is int&
    rref&& r4 = 1; // type of r4 is int&&

    forward完美转发

    先看一段代码:

    #include <utility>
    #include <iostream>
    
    void bar(const int& x)
    {
        std::cout << "lvalue" << std::endl;
    }
    
    void bar(int&& x)
    {
        std::cout << "rvalue" << std::endl;
    }
    
    template <typename T>
    void foo(T&& x)
    {
        bar(x);
        bar(std::forward<T>(x));
    }
    
    int main()
    {
        int x = 10; 
        foo(x);
        foo(10);
        return 0;
    }

    输出:

    lvalue
    lvalue
    lvalue
    rvalue

    foo(10)调用时,进入foo函数,执行bar(x)时,x从右值变成了左值。之所以这样,因为x现在成了一个有名字的变量,所以10是bar(x)的左值参数。如果我们想继续保持10的右值语义,就需要forward,bar(forward<T>(x))。forward的作用就是保持move语义

    #include <utility>
    #include <iostream>
    
    void overloaded(const int& x)
    {
        std::cout << "[lvalue]" << std::endl;
    }
    
    void overloaded(int&& x)
    {
        std::cout << "[rvalue]" << std::endl;
    }
    
    template <class T>
    void fn(T&& x)
    {
        overloaded(x);
        overloaded(std::forward<T>(x));
    }
    
    int main()
    {
        int i = 10; 
        overloaded(std::forward<int>(i));
        overloaded(std::forward<int&>(i));
        overloaded(std::forward<int&&>(i));
    
        fn(i);
        fn(std::move(i));
    
      return 0;
    }

    这段代码输出结果是:

    [rvalue]
    [lvalue]
    [rvalue]
    [lvalue]
    [lvalue]
    [lvalue]
    [rvalue]

    只所以这样,是因为 forward 和引用折叠

  • 相关阅读:
    搭建strom 的开发环境
    maven 的plugin 的使用
    Maven 的dependency 的 classifier的作用
    Maven中的dependency的scope作用域详解
    Supervisor-进程监控自动重启
    websocket 实战
    vue 监听路由变化
    vux-uploader 图片上传组件
    vue 定义全局函数
    判断对象属性的值是否空,如为空,删除该属性
  • 原文地址:https://www.cnblogs.com/zuofaqi/p/10192550.html
Copyright © 2020-2023  润新知