持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情
万能引用
C++11除了带来了右值引用以外,还引入了一种称为“万能引用”的语法;通过“万能引用”,对某型别的引用T&&,既可以表达右值引用,也可以表达左值引用。
该语法有两种使用场景,最常见的一种是作为函数模板的形参:
template<typename T>
void f(T&& param);
其中param就是一个万能引用。 第二个场景则是auto声明:
auto&& var2 = var1;
part1
#include <iostream>
using namespace std;
template<typename F,typename T1,typename T2>
void myfunctemp(F f,T1 t1,T2 t2){
f(t1,t2);
}
void myfunc(int v1,int &v2){
++v2;
cout<<"v1+v2+1 = "<<v1+v2<<endl;
}
int main(){
int j = 70;
myfunctemp(myfunc,20,j);
cout<<"j = "<<j<<endl;
return 0;
}
/*
v1+v2+1 = 91
j = 70
*/
1、t1=20,t2=j(调用拷贝构造,t2和j地址不同了);
2、v1=t1,int &v2=t2(v2是t2的别名,v2和t2地址一样)
所以v2的值加1,j的值不变
part2
#include <iostream>
using namespace std;
template<typename F,typename T1,typename T2>
void myfunctemp(F f,T1 &&t1,T2 &&t2){
f(t1,t2);
}
void myfunc(int v1,int &v2){
++v2;
cout<<"v1+v2+1 = "<<v1+v2<<endl;
}
int main(){
int j = 70;
myfunctemp(myfunc,20,j);
cout<<"j = "<<j<<endl;
return 0;
}
/*
v1+v2+1 = 91
j = 71
*/
1、T1 &&t1=20(相当于int &&t1 = 20);T2 &&t2 =j(相当于int &t2 = j;t2是j的别名,所以t2和j地址一样);
2、v1=t1,int &v2=t2(v2是t2的别名,v2和t2地址一样,所以v2、t2、j的地址一样)
所以v2的值加1,j的值加1
part3
#include <iostream>
using namespace std;
template<typename F,typename T1,typename T2>
void myfunctemp(F f,T1 &&t1,T2 &&t2){
f(t1,t2);
}
void myfunc(int &&v1,int &v2){
++v2;
cout<<"v1+v2+1 = "<<v1+v2<<endl;
}
int main(){
int j = 70;
myfunctemp(myfunc,20,j);
cout<<"j = "<<j<<endl;
return 0;
}
/*
error: cannot bind rvalue reference of type ‘int&&’ to lvalue of type ‘int’
5 | f(t1,t2);
*/
疑问:myfunctemp(myfunc,20,j);我传入的20是右值,为什么右值引用v1不能获取它呢?
因为经过了t1;T1 &&t1=20(相当于int &&t1 = 20);这时t1变量变成了左值
再使用int &&v1 = t1,错误。因为右值引用不能接收一个左值。
part4(forword)
#include <iostream>
using namespace std;
template<typename F,typename T1,typename T2>
void myfunctemp(F f,T1 &&t1,T2 &&t2){
f(std::forward<T1>(t1),std::forward<T2>(t2));
}
void myfunc(int &&v1,int &v2){
++v2;
cout<<"v1+v2+1 = "<<v1+v2<<endl;
}
int main(){
int j = 70;
myfunctemp(myfunc,20,j);
cout<<"j = "<<j<<endl;
return 0;
}
/*
v1+v2+1 = 91
j = 71
*/
使用std::forward能解决part4的报错
因为std::forward可以保持传入参数的类型。传进来的是左值那就是左值,传进来的是右值那就是右值。
T1 &&t1=20(相当于int &&t1 = 20);这时t1变量变成了左值,但是传进来的20是右值,所以std::forward(t1)就是右值,因此右值引用v1能够获取它。
std::forward通过显式指定类型模板参数T1和T2来正常工作,比如std::forward(t1),std::forward(t2)这种写法,因为T1、T2中有一个很重要的信息,就是原始的实参到底是左值还是右值。
std::forward的能力就是保持原始实参的左值或者是右值。
验证1
t1和v1地址相同
#include <iostream>
using namespace std;
template<typename F,typename T1,typename T2>
void myfunctemp(F f,T1 &&t1,T2 &&t2){
cout<<"v1的地址为"<<&t1<<endl;
f(std::forward<T1>(t1),std::forward<T2>(t2));
}
void myfunc(int &&v1,int &v2){
cout<<"v1的地址为"<<&v1<<endl;
++v2;
cout<<"v1+v2+1 = "<<v1+v2<<endl;
}
int main(){
int j = 70;
myfunctemp(myfunc,20,j);
cout<<"j = "<<j<<endl;
return 0;
}
/*
v1的地址为0x7fff7510e2a4
v1的地址为0x7fff7510e2a4
v1+v2+1 = 91
j = 71
*/
传入右值时
#include <iostream>
#include <utility>
#include <boost/type_index.hpp>
using namespace std;
void prinfInfo(int& f){
cout<<"prinfInfo()的参数类型是左值引用"<<endl;
}
void prinfInfo(int&& f){
cout<<"prinfInfo()的参数类型是右值引用"<<endl;
}
template<typename T>
void testF(T&& t){
cout<<"----"<<endl;
using boost::typeindex::type_id_with_cvr;
cout<<"T="<<type_id_with_cvr<T>().pretty_name()<<endl;
cout<<"t="<<type_id_with_cvr<decltype(t)>().pretty_name()<<endl;
cout<<"----"<<endl;
prinfInfo(t);
prinfInfo(std::forward<T>(t));
prinfInfo(std::move(t));
}
int main(){
testF(1);
return 0;
}
/*
----
T = int
t = int &&
----
prinfInfo()的参数类型是左值引用
prinfInfo()的参数类型是右值引用
prinfInfo()的参数类型是右值引用
*/
testF的传入值为1,为右值
T&& t=1(相当于int &&t = 1);
t为接收1的类型所以t = int &&
T记录传进参数1的类型所以T = int
prinfInfo(t);因为此时t已变成了左值,因此调用左值函数
prinfInfo(std::forward(t));因为传入的1为右值,所以forward保持右值,因此调用右值函数
prinfInfo(std::move(t));move强制将左值转为右值,因此调用右值函数
传入左值时
#include <iostream>
#include <utility>
#include <boost/type_index.hpp>
using namespace std;
void prinfInfo(int& f){
cout<<"prinfInfo()的参数类型是左值引用"<<endl;
}
void prinfInfo(int&& f){
cout<<"prinfInfo()的参数类型是右值引用"<<endl;
}
template<typename T>
void testF(T&& t){
cout<<"----"<<endl;
using boost::typeindex::type_id_with_cvr;
cout<<"T="<<type_id_with_cvr<T>().pretty_name()<<endl;
cout<<"t="<<type_id_with_cvr<decltype(t)>().pretty_name()<<endl;
cout<<"----"<<endl;
prinfInfo(t);
prinfInfo(std::forward<T>(t));
prinfInfo(std::move(t));
}
int main(){
int a = 1;
testF(a);
return 0;
}
/*
----
T=int&
t=int&
----
prinfInfo()的参数类型是左值引用
prinfInfo()的参数类型是左值引用
prinfInfo()的参数类型是右值引用
*/
testF的传入值为a,为左值
T& t=a(相当于int &t = a);
t为接收a的类型所以t = int &
T记录传进参数a的类型所以T = int &
prinfInfo(t);因为此时t已变成了左值,因此调用左值函数
prinfInfo(std::forward(t));因为传入的a为左值,所以forward保持左值,因此调用左值函数
prinfInfo(std::move(t));move强制将左值转为右值,因此调用右值函数