为何转发引用传入右值时模板参数未推导为右值引用?
转发引用模板参数推导的疑惑解析
此前已有类似问题,但现有回答未能解决疑问。结合cppreference的模板参数推导规则,我们来分析以下代码中的矛盾点:
首先引用cppreference的推导前调整规则:
在推导开始前,需对P(模板参数类型)和A(实参类型)进行如下调整:
- 若P不是引用类型:
a) 若A是数组类型,则将A替换为数组到指针转换后的指针类型;
b) 否则,若A是函数类型,则将A替换为函数到指针转换后的指针类型;
c) 否则,若A是带cv限定符的类型,则推导时忽略顶层cv限定符;- 若P是带cv限定符的类型,则推导时忽略顶层cv限定符;
- 若P是引用类型,则使用被引用的类型进行推导;
- 若P是指向无cv限定模板参数的右值引用(即转发引用),且对应的函数调用实参是左值,则推导时使用A的左值引用类型替代A;
完成这些转换后,推导过程将尝试找到能使代入模板参数后的P与调整后的A完全匹配的模板实参。
我们来看这段测试代码(其中type_name用于输出类型):
template<typename T> void foo(T && t) { std::cout << type_name<T>(); } int main() { int a = 1; foo(std::move(a)); // 输出int,而非int&& }
你之前的理解误区在于对规则3和匹配目标的误解:
- 错误的逻辑:你认为规则3意味着推导目标是让
T(被引用类型)与调整后的A(int&&)匹配,从而得出T=int&&。 - 正确的逻辑:规则3的“使用被引用的类型进行推导”,是指以
T作为模板参数的载体,但最终的匹配目标是**代入T后的原始P(即T&&)与调整后的A**完全一致。
具体到这个例子:
- 实参
std::move(a)的类型是int&&(右值),由于是右值,规则4不触发,调整后的A就是int&&。 - 我们需要找到
T,使得T&&(代入后的P)等于int&&,显然T=int时满足条件。
这就是代码运行结果显示T=int的原因——cppreference的表述没有偏差,是对规则的匹配目标理解有误。
内容的提问来源于stack exchange,提问作者Elucidase
相关产品推荐
相关产品推荐

