实例代码
// 引用折叠,转发、完美转发,forward
#include <iostream>
using namespace std;
template<typename T>
void myfunc(T &&tmprv) { //万能引用
cout << tmprv << endl;
return;
}
void myfunc1(int v1, int v2){
++v2;// 改变v2值,让其自加1
cout << v1 + v2 << endl;
}
void myfunc2(int v1, int& v2){
++v2;// 改变v2值,让其自加1
cout << v1 + v2 << endl;
}
void myfunc3(int&& v1, int& v2){ // 一个右值参数 一个左值参数
++v2;// 改变v2值,让其自加1
cout << v1 << endl;
cout << v2 << endl;
}
//通过函数模板把收到的参数以及这些参数相对应的类型(比如左值引用、右值引用、比如const)不变的转发给其他函数(转发给myfunc函数),这就叫转发。
template<typename F, typename T1, typename T2>
//myFuncTemp()相当于通过这个来转发。
void myFuncTemp(F f, T1 t1, T2 t2){ //F就是第三方我们要调用的函数, 就是要转发到的目标函数。
f(t1, t2);
}
//改成万能引用
template<typename F, typename T1, typename T2>
void myFuncTemp1(F f, T1&& t1, T2&& t2){ //F就是第三方我们要调用的函数, 就是要转发到的目标函数。
//针对main函数中的调用,myFuncTemp1(myfunc2, 30, j);
// 推断出 : T1 = int , t1 = int &&
// 推断出 : T2 = int&, t2 = int &
f(t1, t2);
}
//改成万能引用 加std::forward
template<typename F, typename T1, typename T2>
void myFuncTemp2(F f, T1&& t1, T2&& t2){ //F就是第三方我们要调用的函数, 就是要转发到的目标函数。
//针对main函数中的调用,myFuncTemp1(myfunc2, 30, j);
// 推断出 : T1 = int , t1 = int &&
// 推断出 : T2 = int&, t2 = int &
f(std::forward<T1>(t1), std::forward<T2>(t2));
//f(t1, t2);
}
void printfInfo(int &t){ //类型是左值引用的形参
cout << "PrintfT()参数类型为左值引用" << endl;
}
void printfInfo(int &&t){ //类型是右值引用的形参
cout << "PrintfT()参数类型为右值引用" << endl;
}
template <typename T>
void TestF(T && t) { //万能引用
printfInfo(t); // 如果t是右值,那么 T = int, t = int && ,但是t本身是左值
// 如果t是左值,那么 T = int &, t = int &; t本身是左值
printfInfo(std::forward<T>(t));
printfInfo(std::move(t)); //左值转右值
}
int main(int argc, const char * argv[]) {
//一: 引用折叠规则
int i = 18; // i的类型是int型, i本身是个左值
myfunc(100); // i是右值, T = int , tmprv = int &&
myfunc(i); // i是左值, T = int &, tmprv = int &
// void myfunc(int& &&tmprv){...} // 我们模拟编译器,认为这是编译器应该给咱们实例化出来的myfunc的样子。
// int& 是一组 , && tmprv是第二组
// void myfunc(int& tmprv){...} //这个是编译器真正给我们实例化出来的myfunc模板函数的样子
//由上面的分析,引出来C++ 11新标准,“引用折叠”
//C++ 中有明确含义的引用只有两种,一种是&左值引用(类型), 一种是带&& 右值引用(类型)
//void myfunc(int& &&tmprv) // 两组,第一组int& : 左值引用, 第二组 &&tmprv,实际是右值引用类型。
//分成两组的, 第一组: 左值引用/右值引用 第二组: 左值引用/右值引用。 那么可以组合成4种情况
//1. 左值引用 --- 左值引用 & &
//2. 左值引用 --- 右值引用 & &&
//3. 右值引用 --- 左值引用 && &
//4. 右值引用 --- 右值引用 && &&
//折叠规则:如果任意一个引用为左值引用,那么结果就为左值引用(传染),否则就是右值引用。
//上面的4种组合折叠后为:
//1. &
//2. &
//3. &
//4. &&
// (1.1) 引用的引用 : void func(int& &&i){ } // "i" : 引用的引用非法
int b = 500;
int &byi = b; //byi是b引用
//int & &byy = byi; //非法 byy是byi的引用, 引用的引用
//int & &byy = b; //非法
// 总结:引用的引用 是有这个概念的,编译器内部会使用到,编译器遇到“引用的引用”,会使用“引用折叠规则”来处理,但作为程序开发者不能使用“引用的引用”,也不可以这么写。
//二:转发、完美转发
int k = 50;
myfunc2(41, k); //执行完成后 i 变成 51,这是对的
int j = 70; //左值
myFuncTemp(myfunc2, 30, j); //这里 j不是 71, 还是70,这说明 myFuncTemp()模板有问题
//那么我们修改myFuncTemp模板函数, 让这个模板函数的参数能够保持 给定实参 的左值性(当然如果j有const属性,我们也希望const属性传递到myFuncTemp中去),这就需要万能引用了
//万能引用: T&& 实参的所有信息都回传递到万能引用当中去,从而让编译器推导出来函数模板最终的形参类型。
//T & ,则实参中只有const属性能传递到函数模板,而实参中的左值和右值性质就无法传递到函数模板中去,所以我们必须用万能引用 : T&&
myFuncTemp1(myfunc2, 30, j); //这里 j是71 ,说明myFuncTemp1()模板传递引用成功
myfunc3(20, j);
int &&youzhi = 80; //右值引用绑右值
//虽然&&youzhi 是绑定到右值的,但是youzhi本身是个左值
//&&youzhi叫右值引用, youzhi是个左值(有地址);
//youzhi是个左值,但是它的类型是右值引用
//也就是说 无论左值引用这种概念还是右值引用这种概念,说得是youzhi的类型,而不是youzhi本身
//如: void ft(int &&w){ ... } // 形参总是左值,(w总是左值),即使它的类型是右值引用。
// myFuncTemp1(myfunc3, 30, j); //报错 提示不能将参数 从"int" 转换为“int &&”
//上面的错误例子引出下面的概念:
//完美转发:让我们可以写接受任意类型实参的函数模板,并将其转发到目标函数,目标函数会接收到与转发函数(myFuncTemp1)所接收到的完全相同(左值性、右值性都要保持)的实参。这就使用到std::forward
myFuncTemp2(myfunc3, 30, j); //正确
//三: std::forward
//c++11 新函数 专门为转发而存在。它要么返回一个左值,要么返回一个右值
//发挥作用的条件: 调用模板函数,模板函数参数是万能引用类型,模板函数负责转发。
//std::forward的能力就是按照参数本来的类型转发
//对std::forward函数的两种理解:
//(1). 实参如果原来是左值,到了形参中还是左值,forward是按照实参原来的类型处理,所以std::forward之后还是个左值。
//(2). 实参如果原来是个右值,到了形参中变成了左值,forward是按照实参原来的类型处理,所以std::forward之后就是个右值
//所以,这里看起来forward有强制把左值转成右值的能力,所以forward其实只对原来是个右值的情况有用。
//std::forward 的能力就是保持原始实参的类型(左值还是右值)
TestF(2); // 2是右值 输出: 左值引用,右值引用,右值引用
int q = 2;
TestF(q); // i是左值 输出: 左值引用,左值引用,右值引用
//实参 是左值还是右值 这个信息会被保存到TestF里的万能引用里边的T类型中去的。
//四: std::move 和 std::forward的区别
//forward: 是强制把一个左值转成一个右值,但是如果实参原来就是左值,那forward什么也不做。(有条件的强制类型转换)
//move: 无条件强制类型转换。
//五: 再谈万能引用
return 0;
}