万能引用
万能引用的格式如下:
template<typename T> void PerfectForward(T&& t) { Fun(t); }
虽然写的是&&和右值引用类似,但是它可以接收左值引用和右值引用
当传过来的是左值,那么
T&&
会折叠为T&
。引用折叠有以下几种情况:
实参 | 形参 | 结果 |
&(左值) | &(左值) | &(左值) |
&(左值) | &&(右值) | &(左值) |
&&(右值) | &&(右值) | &&(右值) |
&&(右值) | &(左值) | &(左值) |
举个栗子😉
void Fun(int& x) { cout << "左值引用" << endl; } void Fun(const int& x) { cout << "const 左值引用" << endl; } void Fun(int&& x) { cout << "右值引用" << endl; } void Fun(const int&& x) { cout << "const 右值引用" << endl; } template<typename T> void PerfectForward(T&& t) { Fun(t); } int main() { PerfectForward(10); // 右值 int a; PerfectForward(a); // 左值 PerfectForward(std::move(a)); // 右值 const int b = 8; PerfectForward(b); // const 左值 PerfectForward(std::move(b)); // const 右值 cout << "----------------------------------" << endl; return 0; }
这里输出为什么都是左值引用呢?
chatgpt的回答:
这是因为在 C++ 中,当一个命名的变量被作为参数传递给函数时,它会被视为左值,即使该变量是通过
std::move
显式转换为右值引用的。我的理解:
因为无论是什么类型作为函数参数接收,就有了载体,就会存在一片空间去存储值了,因此原来的右值有了空间只会就有了地址,就会被认为是左值。
这种情况也被称为不完美转发。
完美转发
经过引用折叠之后,传之前的数据的引用类型和传入之后的引用类型可能发生变化,为了保证类型不会发生变化,完美转发就产生了。
std::forward<>();
对于之前的代码,加上万能转发就能够正确输出类型了。
void Fun(int& x) { cout << "左值引用" << endl; } void Fun(const int& x) { cout << "const 左值引用" << endl; } void Fun(int&& x) { cout << "右值引用" << endl; } void Fun(const int&& x) { cout << "const 右值引用" << endl; } template<typename T> void PerfectForward(T&& t) { Fun(std::forward<T>(t)); } int main() { PerfectForward(10); // 右值 int a; PerfectForward(a); // 左值 PerfectForward(std::move(a)); // 右值 const int b = 8; PerfectForward(b); // const 左值 PerfectForward(std::move(b)); // const 右值 cout << "----------------------------------" << endl; return 0; }
注意:只要使用了完美转发,每一步调用涉及到参数的都必须加上万能转发,不然就会失败。