zoukankan      html  css  js  c++  java
  • lvalue 引用 && rvalue 引用

    C++中的每个表达式要么是lvalue要么是rvalue。lvalue表示一个内存位置,而rvalue表示计算表达式的结果。

    rvalue引用是对有名称变量的引用,并允许变量表示的内存通过lvalue引用来访问。

    rvalue引用是对包含表达式结果的内存位置的引用。

    总之,表达式的结果和函数内定义的变量都属于临时变量,即rvalue。

      int && num {2*x+3};   //rvalue引用表达式的临时结果

      int & num {x=5};    //lvalue引用

    常量引用:

    void GetSet(int &num)
    {
     num += 1;
    }

     Error (active)  initial value of reference to non-const must be an lvalue.

    void GetSet(const int &num)
    {
     num += 1;
    }

    上面代码:只看第一个函数头GetSet(变量)是正确的,但GetSet(常量5)会被报错,因为编译器不允许对常量存在可能潜在的更改行为。

         规定常量作为参数传递时需加const,即意味着不可更改。

    lvalue引用:

    使用lvalue引用形参,可以编写直接访问调用者实参的函数,避免了按值传递中的隐式复制。若不打算修改实参,则只需要给lvalue引用类型使用const修饰符,以避免意外修改参数。

    其实,无论是按值传递、按址传递参数或引用都是编译器的规则,我们需要熟悉参数在不同情况下的传递,好的理解方式就是输出地址来观察。

    void GetSet(int & num)
    {
         num += 1;
    }
    int main()
    {
         int v = 6;
         GetSet(v);
         cout << v<< endl;
         cin.get();
    }

    输出结果: 7 ,num变量值通过引用改变了,可以类比,变量的指针传址方式。

    rvalue引用:

    void GetSet(int && num)
    {
         num += 1;
    }
    int main()
    {
         int v = 6;
         GetSet(v);
         cout << v<< endl;
         cin.get();
    }

    编译器报错:Error (active)  an rvalue reference cannot be bound to an lvalue 

    可知lvalue不能通过rvalue引用,有rvalue引用形参的函数只能通过rvalue实参来调用。

    void GetSet(int && num)
    {
        num += 1;
        cout <<"num="<< num << endl;
    }
    int main()
    {
        int v = 6;
        int c = 3;
        GetSet(v+c);
        cout <<"v="<< v <<" c="<< c <<endl;
        GetSet(5);
        cin.get();
    }

    上面代码编译通过,结果如下:

    编译器会为表达式的结果生成一个临时地址来存储数值。而常量字面值 5 ,被当成一个表达式处理,并且存储在函数引用形参的临时位置。

    输出地址可以看出,常量在rvalue引用的时候和表达式一样处理了,即看做临时变量。

    函数返回值的引用:

    int* GetSet(int num)
    {
        num += 1;
        int result {num};
        return & result;
    }
    int main()
    {
        int v = 6;
        int *ptr=GetSet(v);
        cout <<"ptr="<< *ptr << endl;
    }

    本段代码可以运行,但编译器却给出了如下警告:
    “Warning C4172 returning address of local variable or temporary”

    原因在于:从函数中返回的引用是临时变量的地址,应注意不要从函数中返回局部自动变量的地址。

    可以使用动态内存分配为函数中的变量申请内存,由于动态内存分配的内存是一直存在的(在堆中),除非用 delete 销毁。即如下方式:

    int* GetSet(int num)
    {
        num += 1;
        int *result{ new int {num} };
        return result;
    }
    int main()
    {
        int v = 6;
        int *ptr=GetSet(v);
        cout <<"ptr="<< *ptr << endl;
       delete ptr; }

     观察下面两部分代码的不同:

    一、
    double
    & lowest(double a[], int len) { int j{}; for (int i{ 1 }; i<len; i++) if (a[j]>a[i]) j = i; return a[j]; }
    二、
    double
    & lowest(double a[], int len) { int j{}; for (int i{ 1 }; i<len; i++) if (a[j]>a[i]) j = i; return & a[j]; }

    引用是赋予存在变量的别名,所以实际返回的是数组元素a[j]的引用,而不是改元素包含的值。a[j]的地址用来初始化要返回的引用,该引用是编译器创建的,因为返回类型是引用。

    但返回a[j]和返回&a[j]是不同的。

    返回&a[j],则指定的是a[j]地址,那是一个指针。所以,第二部分代码会被报错如下:
      Error C2440 'return': cannot convert from 'double *' to 'double &' 

    因为指针和引用属于不同类型。

    Const 在传参引用中的潜规则的:

    double refcube(const double &ra)
    {
        return ra*ra*ra;
    }
    
        long edge = 5L;
        double side = 3.0;
        double c1 = refcube(edge);
        double c2 = refcube(7.0);
        double c3= refcube(side+10.0);

    上面代码中的函数调用都会产生临时变量:

      edge虽然是临时变量,类型不正确,double引用不能指向long。

      参数7.0和side+10.0类型虽然正确,但没有名称,编译器将生成一个函数期间调用的匿名临时变量。

    而现在如果我们去掉 const ,会发现编译器会报错:

    Error:a reference of type "double &" (not const-qualified) cannot be initialized with a value of type "long"

    Error:initial value of reference to non-const must be an lvalue 

    Why?

    double refcube(double &ra) 是lvalue引用,要求使用满足类型的左值作为参数。

    那么为何 double refcube(double ra) 会通过编译。

      因为直接调用和使用const一样编译器会产生 临时变量,只不过使用了 const 意味着不能修改参数。

    1. 使用const可以避免无意中修改数据。
    2. 能够处理const和非const实参,否则只能接受非const数据。
    3. 使用const引用使函数能够正确生成和使用临时变量。

    当然此处代码是可以使用rvalue引用或const rvalue引用,但 Long 类型没通过报错如下:

    Error:'double refcube(const double &&)': cannot convert argument 1 from 'long' to 'const double &&'     

  • 相关阅读:
    args 、kwargs不定参数通过列表、元组、字典传递
    内置函数_eval
    python模块之beautifulSoup
    修改jupyter notebook的默认浏览器
    jupyter notebook自动补全功能实现
    在资源管理器中添加一个共享的网络位置
    在word2010中添加带滚动条的文本框
    Outlook 2010中263邮箱客户端设置
    跳跃游戏
    螺旋矩阵
  • 原文地址:https://www.cnblogs.com/yunqie/p/5892252.html
Copyright © 2011-2022 走看看