本章节主要说一下引用折叠的问题,解释一下型别推导形参的问题;
Pre:
先注意以下几点:
- 右值的引用自己本身是左值;
- 先进行型别推导,推导之后产生T,之后在引用冲突的地方进行引用折叠;
- 引用折叠只出现在:型别推导,auto推导,dectype推导,typedef推导这四个地方,其他不会进行引用推导;
基本概念:
对于常规双引用,会直接报错,原因是:
C++中不允许双引用,也就是说,不允许产生引用的引用,例如: int& &a
这种情况
但是引用折叠确实引用的引用;
之前说过,例如:
class widget {};
template<typename T> void func(T&& param);
其中T&&
是万能引用,和之前所以提到的,T会根据param来进行推导,的到T的类型;
根据前三个条款中的万能引用推导:
- 当param为左值时,T为左值引用
T&
; - 当param为右值时,T为非引用
T
;
但是接下来就是一个问题:参数列表的双重引用是如何解决的,即:
- 当param为左值时,T为左值引用
T&
,参数列表为:T& && param
; - 当param为右值时,T为非引用
T
,参数列表为:T&& param
;
第一种情况就是双重引用,需要进行折叠;
折叠引用的条款:如果出现双重引用,则只要其中有一个是左值引用,则整体推断为左值引用。当两个都为右值引用,才会被推导为右值引用;
该条款在forward函数中表现的淋漓尽致;
例如,forward源码如下所示:
template<typename T> T&& forward(typename remove_reference<T>::type& param) {
return static_cast<T&&>(param);
}
并且搭配完美引用使用:
template<typename T> void f(T&& fparam) {
//...
someFunc(std::forward<T>(fparam));
}
例如,f函数内,传入左值和右值widget,之后传入forward函数的形参就有两种:
- 左值引用,为
widget&
; - 右值,为
widget
;
当传入forward为左值引用时,T的型别推导为T&,则forward被特例化为:
T& && forward(T& param) {
return static_cast<T& &&>(param);
}
折叠后发现并没有改变引用形式;
但是当forward为普通类型,T的型别推导为T,则forward被特例化为:
T&& forward(T&& param) {
return static_cast<T&&>(param);
}
这里注意一下,param相当于直接深拷贝了传入参数,但是其实他是一个左值,因此返回的是他的一个深拷贝;
其他情况:
除了型别推导,还有auto、typedef等情况;
例如:
widget w&;
auto&& a=w;
上述会被推导为:
widget& && a;
即为引用;