万能引用的格式如下:
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;
}
注意:只要使用了完美转发,每一步调用涉及到参数的都必须加上万能转发,不然就会失败。