万能引用:
说人话就是这个引用即能引用左值又能引用右值
分为两种:
1. const 左值引用
我们知道,变量的操作都可以概括为读和写,读要求变量的右值属性即可,写则要求变量的左值属性,const引用显然不允许写操作,因此无论左值还是右值都可以绑定上去,(实际上绑定右值相当于拷贝了一份临时变量再绑定)
2. 编译时多态类型的右值引用
编译时多态有两种体现形式,一种是模板,一种是auto和decltype(decltype并不能形成万能引用,但与下面要讲的引用折叠息息相关)
C++ 之后的很多版本都对auto 和decltype进行了拓展,已经很好的能替代模板的一部分功能,这里举个例子:
/*
设计一个进行+操作的方法
*/
//first C中可以拿宏嘛 缺点就是宏函数并不能体现C++的特性,编写麻烦(少个括号就GG),而且其作用域不可控的特性不利于大规模工程开发
#define sum(x,y) ((x)*(y))
//second 当然是模板啦 缺点显而易见 复杂的模板参数,而且在使用时还需要指定结果类型,这不是扯淡嘛,明明编译器都能帮我们干
template<typename sum1T,typename sum2T,typename resultT>
resultT sum(sum1T s1,sum2T s2){
return s1+s2;
}
//third 使用decltype+模板
template<typename sum1T,typename sum2T>
decltype(*static_cast<sum1T*>(nullptr)+*static_cast<sum2T*>(nullptr)) sum(sum1T s1,sum2T s2){
return s1+s2;
}
//or declval返回指定类型的右值引用(不管是否有默认构造方法) 其实等效于上面的操作
template<typename sum1T,typename sum2T>
decltype(declval<sum1T>()+declval<sum2T>()) sum(sum1T s1,sum2T s2){
return s1+s2;
}
//注意下面这种写法是不完善的
template<typename sum1T,typename sum2T>
decltype(sum1T()+sum2T()) sum(sum1T s1,sum2T s2){ //原因显而易见 sum1T sum2T不一定有默认构造方法
return s1+s2;
}
//forth 使用后置返回值+decltype+auto形式彻底摆脱模板
auto sum(auto s1,auto s2)->decltype(s1+s2){
return s1+s2;
}
//fifth 终极简化 完全删去decltype
auto sum(auto s1,auto s2){
return s1+s2;
}
回归正题:
T&& or auto&& 都是万能引用
- 注意这个万能是初始绑定万能,一旦确定其左右值属性就不能再更改了
- 推导时那个T/auto被替换为{左值 T& 右值 T}
引用折叠
C++不允许引用的引用这一说法,但第二类万能引用 and decltype很可能产生引用叠加问题
如:
//auto/模板
void test(auto &&x){
}
int main(){
int b=10;
test(10) //->相当于 int &&正常
test(b); //->相当于int& && 不正常了,出现左值的右值了 这时我们把左值的右值换为左值 即相当于int &
int&& c =15;
int&d =b;
decltype(c) &&e = xxx;//->相当于int&& && 右值引用的右值引用 替换为右值引用 即int&&
decltype(c) &f=xxx;//右的左->左
decltype(d) &g=xxx;//左的左->左
}
总结一句话就是 只有右的右是右值引用 其他都是左