C++11中的rvalue references(一):左值和右值(lvalue and rvalue)

来源:互联网 发布:网络兼职遭遇刷单陷阱 编辑:程序博客网 时间:2024/06/05 18:14

*

“If you’re an experienced C++ programmer and are anything like me, you initially approached C++11 thinking, “Yes, yes, I get it. It’s C++, only more so.” But as you learned more, you were surprised by the scope of the changes. auto declarations, range-based for loops, lambda expressions, and rvalue references change the face of C++, to say nothing of the new concurrency features. And then there are the idiomatic changes. 0 and typedefs are out, nullptr and alias
declarations are in. Enums should now be scoped. Smart pointers are now preferable to built-in ones. Moving objects is normally better than copying them.
- Effective Modern C++ by Scott Meyers*

*
这是大牛在Effective Modern C++中的一段对新C++特性的总结。其中rvalue references是一个比较核心的改进,对某些情况下对C++代码的效率很有帮助。最近在看相关的文档,笔者想写篇关于rvalue references的介绍性文章;准备分两部分:第一部分介绍下什么是rvalue和rvalue references,第二部分介绍它的应用。

lvalue 和 rvalue

lvalue和rvalue的概念最初来自C语言,后来C++对它们有所扩展。最初,在C里lvalue和rvalue貌似分别指一个赋值表达式的左边和右边值。(“L” stands for “left” and “R” stands for “right“)C++引入后,这个名字里左边啊,右边啊,就变得不那么清晰了;也就是C++里它们不再局限于赋值表达式的左边和右边了。
首先,有一点是肯定的,C++里一个表达式要么是lvalue的,要么是rvalue。这里有一点要强调,lvalue和rvalue的是表达式的属性,不是object的属性。(C++03 3.10/1 says: “Every expression is either an lvalue or an rvalue.”)
lvalue一般是有可以寻址的存储位置,它在表达式后还会存在(persist beyond a single expression)。rvalue一般是临时性的,在表达式后就会消失;所以rvalue是无法得到地址的,原因是如果可以得到临时东西的地址,那后续访问这个地址将是灾难性的。
还有一个判断lvalue和rvalue的小窍门是试着对表达式取地址(&);能合法取地址的是lvalue,不能取的或者得到荒谬结果的是rvalue。比如,&x,&x[0]都是合理的,所以x和x[0]都是lvalue;而&7,&(x+1),&(x+y)都是非法的,所以7,(x+1),(x+y)都是rvalue。

lvalue 和 rvalue的例子

下面举一些常见lvalue 和 rvalue的表达式。
以下是常见的lvalue:

int var = 0;var = 1 + 2; // ok, var is an lvalue hereint* p1 = &var; // ok, var is an lvalueobj , *ptr , ptr[index], ++x; // lvalue// function returned is rvalue, except it returns a referenceint x;int& getRef () {        return x;}getRef() = 4;     // lvalue, as getRef() returns a reference

常见的rvalue:

1 + 2;var + 1 = 2 + 3; // error, var + 1 is an rvalueint* p2 = &(var + 1); // error, var + 1 is an rvaluex++;// function returned is rvalue, except it returns a referenceint x;int getVal (){    return x;}getVal();    // rvalueUserType().member_function(); // ok, calling a member function of the class rvalue

上面有两点要注意的。第一是++x和x++。这两哥们很像,平时几乎没区别(除了一个是先加再取x的值,一个是先取x的值后加)。但其实这俩是完全不同的表达式:前者是lvalue后者是rvalue!++x和x++都是增加x值,但是++x返回的是原来的x,++后x依然存在;而x++返回的只是一个x的临时copy!
第二个要注意的是函数。只有返回引用时,函数才是lvalue;其他情况都是rvalue。

运算符重载中的 lvalue 和 rvalue问题

上面的例子中没有涉及到运算符重载;其实运算符重载和函数是一样的规则——只有返回引用时,运算符重载才是lvalue;其他情况都是rvalue。

reference operator[] (size_type n);vector<int> v(10, 1729); v[0];  // is an lvalue because operator[]() return reference int& .string operator+ (const string& lhs, const string& rhs);string s(“foo”);string t(“bar”);s + t;   // is an rvalue because operator+() returns string (and &(s + t) is invalid).string& operator= (const string& str);s=t=p; // makes sense; as operator= is lvalue

lvalue 和 rvalue const属性

lvalue 和 rvalue 都可以是const或non-const的。比如:

string one(“cute”);const string two(“fluffy”);string three() { return “kittens”; }const string four() { return “are an essential part of a healthy diet”; }one;     // modifiable lvaluetwo;     // const lvaluethree(); // modifiable rvaluefour();  // const rvalueconst string&=three();

这里最关键的是引用(Type &)的变化。引用bind到 lvaue上,可以用来观察和修改变量值;所以非const引用不能作用于const lvaue和rvalue。作用于rvalue意味着可以修改临时变量值,这是绝对禁止的。
const引用(const Type &)可以bind到任何value上,lvalues, const lvalues, rvalues, and const rvalues (and can be used to observe them).

Refences

https://accu.org/index.php/journals/227
https://blogs.msdn.microsoft.com/vcblog/2009/02/03/rvalue-references-c0x-features-in-vc10-part-2/

阅读全文
0 0
原创粉丝点击