前面我们介绍到,当函数模板的形参的型别为万能引用时,当传递给该函数模板的实参是个左值时,则推导出的形参的类型型别为左值引用,当传递给该函数模板的实参是个右值时,则推导出的形参的类型型别为右值引用。还有在实现完美转发时,当传递给调用者的实参是左值时,使用std::forward()被调者的形参的型别为左值引用,当传递给调用者的实参是右值时,使用std::forward()被调者的形参的型别为右值引用。我们先看一个简单的例子:
template<typename T>
void _fun(T&& t) {};
当我们为_fun()传递实参时,函数模板T的推导结果型别中,会把传给传递给该函数是左值还是右值的信息编码到T里面去。因此,当我们传递的实参是个左值,T的型别被推导为T&,当我们传递的实参是个右值,T的型别被推导为T。但是为什么传递的实参是左值时,t的类型型别为左值引用呢?答案:发生了引用折叠。
因为存在右值引用和左值引用,使用引用折叠分四种情况:右值——右值-->右值引用 左值——左值-->左值引用 右值引用——左值引用-->左值引用 左值引用——右值引用-->左值引用 。也就是说任意引用为左值引用,则结果为左值引用,否则结果为右值引用。
std::forward()的实质是应用了引用折叠和强制类型转换。
template<typename T>
T&& forward(std::remove_reference_t<T>& t) {
return static_cast<T&&>(t);
}
以上是std::forward()的简单实现。我们可以发现不管传递的是左值还是右值,都会进行引用折叠。但是只有传递的是右值时才进行强制类型转换。
引用折叠出现的语境有四种:第一,模板的类型推导。 第二,auto的类型推导(实质就是模板的类型推导)。第三,typedef和别名声明(using)。第四,decltype的使用