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 &&'     

原文地址:https://www.cnblogs.com/yunqie/p/5892252.html