如下来分析使用右值引用 + 新增的引用折叠推导规则,如何来达成完美转发
进行转发的函数模板定义如下
其中 WrapFunc 函数模板将参数转发给 InnerFunc 函数
template<class A>void WrapFunc(A &&a)
{
InnerFunc(static_cast<A &&>(a));
}
先给出转发结果,后面分析原因
转发结果
non-const左值 ---> non-const左值
const左值 ---> const左值
non-const右值 ---> non-const右值
const右值 ---> const右值
疑问点1:函数模板的函数形参为右值引用形式,他能正确的转发左值吗?
函数模板的函数形参为右值引用形式,并不是说该函数模板实例的函数形参为右值引用
我们使用的是函数模板实例化后的模板函数,而不是该函数模板
因此,要看进行了进行实参推演后得到的函数模板实例的函数形参类型,才能决定他能接受什么值类型的参数
引用折叠
由疑问点1,我们知道,要判断该函数模板是否能完美转,要看在使用该函数模板进行实参推演时
推演得到的模板函数的函数形参是什么。因此是否能正确的推演出合理的类型,是该函数模板是否
能完美转发的关键,而引用折叠正是C++11新增的,在进行实参推演时使用的一个规则
设 T 为模板形参,A 为一个已经定义的类型
如下分析引用折叠的规则
若函数模板的函数形参为 T&
若 T 被推演成 A,函数形参 A& 无需引用折叠,得到目标类型为 A&
若 T 被推演成 A&,函数形参 A& & 进行引用折叠后,得到目标类型为 A&
若 T 被推演成 A&&,函数形参 A&& &进行引用折叠后,得到目标类型为 A&
若函数模板的函数形参为 T&&
若 T 被推演成 A,函数形参 A&& 无需引用折叠,得到目标类型为 A&&
若 T 被推演成 A&,函数形参 A& && 进行引用折叠后,得到目标类型为 A&
若 T 被推演成 A&&,函数形参 A&& && 进行引用折叠后,得到目标类型为 A&&
完美转发的原因分析
匹配类型:A&
根据实参推演规则,函数模板的函数形参为 非 cv 限定的右值引用形式,且实参为左值
则匹配类型为实参类型的引用类型,因此匹配类型为 A&
参数化类型:T
根据实参推演规则,函数模板的函数形参为引用形式,则参数化类型为 T
目标类型:A&
实参推演后,T 被推演成 A&
目标类型为 A& &&,进行引用折叠后为 A&
转发效果
转发给 InnerFunc 的是 形参 A &a,还是 non-const 左值
若实参为 A类型的 const左值
匹配类型:const A&
根据实参推演规则,函数模板的函数形参为 非 cv 限定的右值引用形式,且实参为左值
则匹配类型为实参类型的引用类型 且 保留 cv 限定符,因此匹配类型为 const A&
参数化类型:T
根据实参推演规则,函数模板的函数形参为引用形式,则参数化类型为 T
目标类型:const A&
实参推演后,T 被推演成 const A&
目标类型为 const A& &&,进行引用折叠后为 const A&
转发效果
转发给 InnerFunc 的是 形参 const A&,还是 const 左值
若实参为 A类型的 non-const 右值
匹配类型:A
虽然函数形参为右值引用,但实参为右值,因此匹配类型就是该实参的类型,即为 A
参数化类型:T
根据实参推演规则,函数模板的函数形参为引用形式,则参数化类型为 T
目标类型:A&&
实参推演时,由于实参为 A类型的右值,被推演成 A&&
目标类型为 A&& &&,进行引用折叠后为 A&&
转发效果
具名的右值引用形参 A &&a 为左值,但经过 static_cast<A &&> 转换后变成了右值
因此转发给 InnerFunc 的,还是 non-const 右值
若实参为 A类型的 const 右值
匹配类型:const A
函数形参为引用形式,因此匹配类型为保留 cv 限定符
虽然函数形参为右值引用,但实参为右值,因此匹配类型就是该实参的类型,即为 const A
参数化类型:T
根据实参推演规则,函数模板的函数形参为引用形式,则参数化类型为 T
目标类型:const A&&
实参推演时,由于实参为 const A类型的右值,被推演成 const A&&
目标类型为 const A&& &&,进行引用折叠后为 const A&&
转发效果
具名的右值引用形参 const A&&a 为const左值,但经过 static_cast<const A&&> 转换后变成了const右值
因此转发给 InnerFunc 的,还是 const 右值