为何模板右值引用形参可绑定左值?普通右值引用则不可?
为啥模板里的右值引用能绑定左值?
嘿,这个问题问到C++11引用特性的核心点上了!这可不是编译器开了后门,而是转发引用(Forwarding Reference,以前也叫“万能引用”)加上引用折叠规则共同作用的结果,咱们一步步掰扯明白:
先回顾普通右值引用的限制
你说的没错,普通的右值引用Foo&&是“死心眼”的——它就是专门用来绑定右值(比如临时对象、字面量)的,硬塞左值肯定会报错,就像你写的这段代码:
void func(Foo &&f) {} int main() { Foo f; func(f); // 编译器报错:无法把左值绑定到右值引用 }
这时候Foo&&的类型是固定的,没有任何推导空间,编译器当然会严格执行右值引用的绑定规则。
模板里的T&&根本不是普通右值引用!
而模板中的T &&arg,本质是转发引用——它的类型会根据你传入的实参动态变化,关键就在模板参数的推导过程:
当你传入左值Foo f给funcTemp(f)时:
- 模板参数
T会被推导成Foo&(注意,是左值引用类型!) - 这时候形参的类型就变成了
Foo& &&,这时候C的引用折叠规则就生效了:
C不允许“引用的引用”存在,所以会按照以下规则折叠成单一引用:- 只要其中有一个是左值引用,最终结果就是左值引用:
T& &、T& &&、T&& &全都会变成T& - 只有当两个都是右值引用时,才会保留右值引用:
T&& &&→T&&
- 只要其中有一个是左值引用,最终结果就是左值引用:
所以推导后的Foo& &&会折叠成Foo&——也就是一个普通的左值引用,自然就能绑定左值f了!
再验证一下右值的情况
如果我们传入右值,比如funcTemp(Foo()):
- 模板参数
T会被推导成Foo(非引用类型) - 形参类型就是
Foo&&,也就是普通右值引用,完美绑定临时对象这个右值。
最后划个重点:转发引用的前提
不是所有模板里的&&都是转发引用,必须满足两个条件:
- 模板参数是被推导的类型(也就是
T是推导出来的,不是固定死的) - 声明形式必须是
T&&(不能是std::vector<T>&&这种带其他类型包装的)
内容的提问来源于stack exchange,提问作者Pei Hsiang Hung
相关产品推荐
相关产品推荐

