目录
- 一. 前言
- 二. 引用折叠
- 引用折叠的规则
- 三. 完美转发
- 完美转发适用场景
- 完美转发底层实现
- 思考1
- 思考2
一. 前言
在函数传参时,如果想保持某个参数的属性不改变,需要完美转发,而完美转发的实现需要折叠引用的帮助
二. 引用折叠
在语法上,c++不能定义引用的引用,如 int & && r=i,这样会报错,但是通过模板或typedef的类型可以构成引用的引用,
引用折叠的规则
- 除了右值引用的右值引用,其他组和都是左值
-
T& & → T& (左值引用的左值引用折叠为左值引用)
-
T& && → T& (左值引用的右值引用折叠为左值引用)
-
T&& & → T& (右值引用的左值引用折叠为左值引用)
-
T&& && → T&& (右值引用的右值引用折叠为右值引用)
- 通过引用规则我们发现,T&&的结果可以保持T本身的属性,所以T&&也被称为万能引用,
三. 完美转发
完美转发适用场景
从下图可以看出,在最开始Function函数中传入的是右值,但在Func中却变成了左值,这是由于右值表达式的属性是左值,
这样就破坏了最开始传入的值的属性
想要保持表达式的属性,就需要用完美转发来解决,见下图
完美转发底层实现
完美转发的本质是函数模板,通过引用折叠实现,在forward内部进行强转,然后再引用折叠返回,
std::remove_reference_t& t 移除T最外层的引用修饰,使不管传入什么类型,t的类型都是左值引用类型
思考1
思考1:为什么需要保证t的类型是左值引用?
直接用原类型不行么 T&& forward(T&& t)
传入左值时,T被推导为T&,T&->T&,没有问题
传入右值时,T被推导为T,T->T&&,左值向右值转化,危险操作
template <class T>
T&& forward(std::remove_reference_t<T>& t)
{//std::remove_reference_t<T> 类型萃取模板,移除T的最外层引用修饰,return static_cast<T&&>(t);·
}
template <class T>
void Function(T&& t)
{//Func(std::forward<T>(t));Func(forward<T>(t));//Func(t);
}int main()
{int&& n = 10;Function(n);return 0;
}
思考2
思考2:为什么需要用std::remove_reference_t& 来移除最外层修饰的引用,
T&& forward(T& t) 不行么?
传入左值时
T& && forward(T& & ) 实例化-> T& forward(T & ) 没有问题
传入右值时
T&& && forward(T&& & ) 实例化-> T&& forward(T & ) 看似没有问题
虽然 int&& & 理论上可以折叠为 int&,但 C++ 类型系统禁止直接声明这种组合: