C++11读书笔记—5(右值系统)

一、C++11的三种值

左值与右值最快分辨是看能否为表达式取地址。能,左值;不能,右值。(其实左值放等号右边也没什么大不了的。)

如 int i = b+c; i能取&i吗?能,左值。。。b+c能写&(b+c)吗?不能,右值。(书上说左值右值没有明确定义,仅用判断)

右值有两个概念:将亡值与纯右值。

纯右值:非引用返回的临时变量;运算表达式产生的临时变量,原始字面量,lambda表达式

将亡值:将被移动的值,T&&函数返回值,std::move返回值,转换为T&&的类型的转换函数返回值。

C++11中所有的值必是左值,纯右值,将亡值之一。

二、右值引用&&

0. 从左值引用开始

T& a = ReturnRightV();//肯定这样会报错。
const T& b = ReturnRightV(); //但常引用在C++98标准中是个奇葩的存在。这样可以。
常量引用在C++98开始定义时就是万能的,能接受左值,右值的初始化,只不过是只读的。
const bool & i = TRUE; 

1.类推右值引用

下面这语句就是右值引用
T&& a= ReturnRightV();
ReturnRightV()不能取地址,典型的右值。只要a还活着右值临时量将会一直存活下去。
我们可以这么理解,左值引用是具名变量的别名;右值引用是匿名变量的别名。那么起别名干什么呢?就是为了解决上一篇文章中函数返回过于繁杂的问题。
我们比较下
T&& a= ReturnRightV();//不用了那么复杂的过程了。
T b = ReturnRightV(); //按原标准,无编译器优化,得调用copy构造函数,析构函数
注意事项:
既然是引用,那声明时必须附初始值;
既然是右值,那么int a; int && b =a; 搞a这样的左值就不行。

看下面这个例子:
(这个函数中,在实际应用中他也可以传入左值做形参)
void AccRvalue(A && a)
{
	A temp = std::move(a);
}
这里std::move的作用是强制一个左值,变为右值。如果有必要这里还要添加一个移动构造函数。

A (A && a){/*实现移动构造函数*/}
这里有一个机制,如果移动不成功,他会执行一常数左值引用为参数的拷贝构造函数。所以推荐程序员在写完移动构造函数后再写一个拷贝构造函数。
我怎么觉得这又是一个坑呢????还好吧,毕竟能跑通。
下面的表摘自机械工业出版社的《深入理解C++11》79页,C++11中引用类型及其可以引用的值类型

 非常量左值常量左值非常量右值常量右值用途
非常量左值YNNNC++11前常见的使用方法
常量左值YYYY万能的类型,但是局限于常量不能改
非常量右值NNYN用于移动语义
常量右值NNYY 

2.what?不是所有&&都仅代表右值引用

Widget&& var1 = someWidget; // 这里, “&&” 表示rvalue reference
 
auto&& var2 = var1; // 这里, “&&” 不代表 rvalue reference
 
template<typename T>
void f(std::vector<T>&& param); // 这里, “&&” 表示rvalue reference
 
template<typename T>
void f(T&& param);               // 这里, “&&” 不代表 rvalue reference

声明中带 “&&” 的,可能是lvalue references 或者 rvalue references 的引用可以绑定到任何东西上。这种灵活的引用值得单独给它们起个名字。我称它们为 universal references。

如果一个变量或者参数被声明为T&&,其中T是被推导的类型,那这个变量或者参数就是一个universal reference。(此标准严苛,即使是const T&& 这种附加一点条件的都不是universal reference

造成这种情况原因在于C++不允许出现引用的引用,但是一遇到模板就会遇到这种问题。C++11引入了一个叫做“引用折叠”(reference collapsing)的规则来处理某些像模板实例化这种情况下带来的"引用的引用"的问题。

  • 一个 rvalue reference to an rvalue reference 会变成 (“折叠为”) 一个 rvalue reference.
  • 所有其他种类的"引用的引用" (i.e., 组合当中含有lvalue reference) 都会折叠为 lvalue reference.
<span style="font-weight: normal;">	int mynamestringisverylong;
	int x;
	int && var1 = mynamestringisverylong; //一个左值搞到右值引用上当然会出错
	auto && var2 = mynamestringisverylong; //var2是个universal reference,你看没报错
	decltype(x) && var3 = mynamestringisverylong;//推导后,发现一个左值搞到右值引用上还是会出错
	// 如果要想玩,可以利用std::move()这么搞
	int&& var4 = std::move(mynamestringisverylong);
</span>

这么长的名字是为了方便看VS报错信息,下图


3.&&使用总结如下

因为太复杂,我就不做进一步推论过程,直接将答案写出来。
(1)左值和右值独立于他们的类型, 右值引用类型也可能是左值也可能是右值。
(2)auto&&或函数参数类型自动推导的T&& 是一个未定的引用类型,被称为universal references
(3)所有的右值引用叠加到右值引用上仍然是右值引用,其他引用折叠为左值引用。当T&&为模板参数时,输入左值会变为左值引用,输入右值会变为右值引用。
(4)编译器会将已命名的右值引用视为左值,未命名的视为右值。

三、forward和完美转发

所谓完美转发,就是指函数模板中,完全按照模板的参数类型将参数传递给函数模板中调用的另一个函数。
template <typename T>
void Forwarding(T t) { ProcessValue(t); }
就是传入Forwarding的是左值,ProcessValue接受左值; 就是传入Forwarding的是右值,ProcessValue接受右值。而不产生而外开销。
C++11提供了std::forward
template <typename T>
ProcessValue(T&& t){}
template <typename T>
ProcessValue(T& t){}
template <typename T>
void Forwording(T t)
{
	ProcessValue(t); 
	ProcessValue(std::forward(v));
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值