如果一个模板函数的形参是通用引用,那么: 1.
如果传入的实参是左值,那么推导出来的形参就是左值引用 2.
如果传入的实参是右值,那么推导出来的形参就是不带引用的普通形参
1
2
3
4
5
6Widget widgetFactory(); // function returning rvalue
Widget w; // a variable (an lvalue)
func(w); // call func with lvalue; T deduced
// to be Widget&
func(widgetFactory()); // call func with rvalue; T deduced
// to be Widget
引用折叠
但是为什么在里面加上std::forward
却又可以区分出来左值引用和右值引用?
1 |
|
上面的推导逻辑,在编译器内部就叫做引用折叠(reference collapsing
),也就是两个引用化简为一个引用。
既然引用分为左值和右值引用,那么就会有 4 种组合:
- 两个左值引用
- 一个左值引用,一个右值引用
- 一个右值引用,一个左值引用
- 两个右值引用
其化简原则如下:
- 只要其中一个是左值引用,那么结果就是左值引用
- 只有两个都是右值引用时,结果才是右值引用
好了,现在来看std::forward
的简易实现:
1 | template<typename T> // in |
下面来推导传入左值的情况,当传入的是v1
按照前面的规则,则param
会被推导为std::vector<int>&
,那么对于forward
就是:
1 | std::vector<int>& && forward(typename |
那么按照引用折叠的规则,最终返回的就是std::vector<int>&
。
而如果传入的是右值,那么param
会被推导为std::vector<int>
,对于forward
就是:
1 | std::vector<int> && forward(typename |
这种情况下,不会发生引用折叠,那么就直接返回一个右值引用即可。
引用折叠发生的场景
引用折叠发生在以下 4 种场景种:
- 模板推导过程种
auto
推导- 使用
typedef
- 使用
decltype
auto
推导
auto
推导和模板推导很类似:
1 | Widget w; // a variable (an lvalue) |
typedef
假设有一个模板类设计如下:
1 | template<typename T> |
那么传入的参数不同,则会得到相应的左右值引用:
1 | Widget<int&> w; |