lvalue和rvalue的区别

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lvalue和rvalue的区别相关的知识,希望对你有一定的参考价值。

俺以前的在C入门的理解是lvalue是可以被赋值、修改的,rvalue是只读的。
但在vc++2010入门书里面是这样解释的:
lvalue:指的是内存中持续存储数据的一个地址。
rvalue:临时存储的表达式结果。
以下三个表达式:
a=b+c;
b=++a;
c=a++;
其中:语句1:表达式"b+c"是一个rvalue.
语句2:表达式"++a"是一个lvalue;
语句3:表达式"a++"是一个rvalue;
因为基本上每章都有lvalue和rvalue的讨论,俺看了半天也不明白其区别和含义,这书我快看不下去了。求高手解惑,如果能浅显易懂的让俺明白最好了。
小弟谢谢了

lvalue 就是 left value 左值
rvalue 就是 right value 右值

之所以叫这么个名字, 是因为 左值 可以放在 = 的左边, 而右值不可以.

int a;
const int b = 3;

a = 10; // a可以放在=的左边, 也就是说可以赋值, 那么就是"左值"
b = 10; // const 类型不可以赋值, 也就是说不可以放在=的左边, 所以是"右值"

左值包括所有的 "非const" 变量.
右值包括所有"不能赋值"的东西, 包括const 变量, 临时变量, 常数等等.

b+c 返回临时对象, 右值, 所以你不可以 b+c = x;
++a 前 ++和--都返回本身的引用, 属于左值, 所以可以(++a) = x; 虽然这么写没有意义.
a++ 后 ++和--都返回临时变量, 是"右值"

是左值还是右值, 只要看他能不能被赋值就可以了.
至于const对象, 你非要用强制类型转换让他变成左值也不是不可以..
参考技术A ++a 是a先加了1,然后把a自身复制给b,也就是说运算结果有地址,即a的地址,因此是l-value
a++是把a的值赋给b,然后a再加1,但是按照运算规则应该是a++先运算,然后再赋值,这样就不得不引入一个临时变量保存a的原始值,这个临时变量无地址,即r-value

如果我通过通用引用接受参数,is_rvalue_reference 和 is_lvalue_reference 中的一个是不是为真?

【中文标题】如果我通过通用引用接受参数,is_rvalue_reference 和 is_lvalue_reference 中的一个是不是为真?【英文标题】:If I accept a parameter via universal reference, is exactly one of is_rvalue_reference and is_lvalue_reference true?如果我通过通用引用接受参数,is_rvalue_reference 和 is_lvalue_reference 中的一个是否为真? 【发布时间】:2019-07-12 00:19:15 【问题描述】:

这段代码有没有可能打印“nether”?

using namespace std;
template<typename T>
void foo(T&& t) 
    if constexpr (is_lvalue_reference_v<T>) 
        cout << "lv" << endl;
     else if constexpr (is_rvalue_reference_v<T>) 
        cout << "rv" << endl;
     else 
        cout <<"neither" << endl;
    

【问题讨论】:

另一方面,is_lvalue_reference_v&lt;T&amp;&amp;&gt;is_rvalue_reference_v&lt;T&amp;&amp;&gt; 中的一个为真。 @songyuanyao 哎呀,对不起。 @L.F.美好的。我明白你的意思了。 【参考方案1】:

这段代码有没有可能打印“nether”?

是的,任何时候将右值传递给foo 并且没有给出明确的模板参数时,都不会打印“neither”:

foo(42);  // "neither" is printed because T is deduced as int

或者当显式指定非引用类型时:

int i=0;
// "neither" is printed because T is explicitly specified as int:
foo<int>(std::move(i));

虽然T 可以是非引用类型,但t 的类型将始终是引用类型。 t的类型有三种可能:

    T是一个值类型(即int):t的类型是int&amp;&amp;;对int 的右值引用。 T 是一个左值引用(即int&amp;):t 的类型是int&amp; &amp;&amp;,它折叠成int&amp;;对int 的左值引用。 T 是一个右值引用(即int&amp;&amp;):t 的类型是int&amp;&amp; &amp;&amp;,它折叠成int&amp;&amp;;对int的右值引用。

这是转发引用的工作机制。如果将右值传递给foo,则T 将被推导出为值类型。如果你传递一个左值,那么T 将被推断为一个左值引用类型。

【讨论】:

很好的答案,但我想您要注意foo&lt;int&gt;(i) 将无法编译(因此它不会打印任何内容)。传递std::move(i) 即可解决。 @JohannesSchaub-litb 嘿,它最初有std::move,但我虽然“让我们让它显式参数与推断的参数不同”并打破它。加回来了。【参考方案2】:

这段代码有没有可能打印“nether”?

是的。

foo(5); // neither

如果我通过通用引用接受参数,is_rvalue_reference 和 is_lvalue_reference 中的一个是否为真?

参数t 将具有右值引用类型或左值引用类型。另一方面,T 的类型将根据扣除和reference collapsing 规则而有所不同。如果改为将 is_lvalue/rvalue_reference&lt;T&gt; 更改为 is_lvalue/rvalue_reference&lt;decltype(t)&gt;,则永远无法执行 else 路径。

【讨论】:

【参考方案3】:

这段代码有没有可能打印“nether”?

是的。根据forwarding reference的类型推导规则,T在被传递左值时,会被推导出为左值引用类型,T被传入右值时,被推导出为非引用类型。例如

int i;
foo(i); // T is deduced as int&
foo(0); // T is deduced as int

LIVE

另一方面,"rv" 不会被打印,除非明确指定模板参数。

另一方面(再次),如果您检查函数参数t 的类型,它将是左值引用类型或右值引用类型; "neither" 不会被打印出来。

int i;
foo(i);        // T is deduced as int&, the type of t is int& (int& && -> int&)
foo(0);        // T is deduced as int, the type of t is int&&
foo<int&&>(0); // T is specified as int&&, the type of t is int&& (int&& && -> int&&)

LIVE

【讨论】:

以上是关于lvalue和rvalue的区别的主要内容,如果未能解决你的问题,请参考以下文章

如果我通过通用引用接受参数,is_rvalue_reference 和 is_lvalue_reference 中的一个是不是为真?

C++ lvalue,prvalue,xvalue,glvalue和rvalue详解(from cppreference)

C++,在函数中采用 const lvalue 和 rvalue 引用

在 RValue 对象上调用 LValue ref 限定成员函数

c ++接收const lvalue和rvalue引用参数而不重载[重复]

c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)