【C/C++】【C++11】引用折叠和完美转发

引用折叠规则

  • 引用折叠 C++11新标准
  • C++中有明确含义的引用只有两种,一种是&左值引用类型,一种是带&&右值引用类型;
  • 折叠规则:如果任意一个引用为左值引用,那么结果就为左值引用,否则就是右值引用
    一共下面四种情况
    & &
    & && 
    && &
    && &&
    
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
template <typename T> //T是类型模板参数,T是由类型的
void func(T &&tmp)//tmp形参,形参是有类型的 tmp形参的类型和T模板参数的类型不一样
{
	//T的类型不仅仅和调用者提供的实参有关系,还和tmp的类型有关;
	cout << "--------------s-----------------" << endl;
	using boost::typeindex::type_id_with_cvr;
	cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl; //显示T类型
	cout << "tmp = " << type_id_with_cvr<decltype(tmp)>().pretty_name() << endl; //显示tmp类型
	cout << "---------------e----------------" << endl;
}
int main()
{  
	int i = 19;
	func(i); //i是左值, T = int &, tmp = int &
	func(100);  //i是右值,T = int, tmp = int &&
	
	//i是左值时
	//void func(int & &&tmp){...}  int&一组 &&tmp是一组,我们认为编译器实例化的结果
	//void func(int & tmp){...} 编译器实际实例化的
	//& &&变成&  即为引用折叠 C++11新标准
	//一共下面四种情况
	//& &
	//& && 
	//&& &
	//&& &&
	//折叠规则:如果任意一个引用为左值引用,那么结果就为左值引用,否则就是右值引用
	return 0;
}

引用的引用

int b = 5;
int& bb = b;
//int&& bx = bb; 不存在引用的引用
//编译器内部在进行函数模板推断的时候可以有,有引用折叠,有引用的引用这个叫法,但是没有这个写法;

转发、完美转发

转发1


#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
void func(int m, int n)
{
	++n;
	cout << m + n << endl;
}

//函数模板:要把收到的参数以及这些参数相应的类型(比如左值引用还是右值应用,比如const)不变的转变给其他函数(转发给func函数),就是转发
template <typename F, typename T1, typename T2>
void func_test(F f, T1 t1, T2 t2) //F就是要转发到的目标函数
{
	//转发
	f(t1, t2);
}

int main()
{  
	int i = 50;
	//func(42, i);
	int j = 70;
	func_test(func, 20, j);
	cout << j << endl;
	return 0;
}

转发1的问题


#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
void func(int m, int &n)
{
	++n;
	cout << m + n << endl;
}
//函数模板:要把收到的参数以及这些参数相应的类型(比如左值引用还是右值应用,比如const)不变的转变给其他函数(转发给func函数),就是转发

template <typename F, typename T1, typename T2>
void func_test(F f, T1 t1, T2 &t2) //F就是要转发到的目标函数
{
	//转发
	f(t1, t2);
}

int main()
{  
	int i = 50;
	//func(42, i);
	int j = 70;
	func_test(func, 20, j);
	cout << j << endl;
	return 0;
}

转发2

  • T&&,实参的所有信息都会传递到万能引用当中,从而让编译器推导出来函数模板最终的形参类型;
  • T& 实参中只有const属性能传递到函数模板,而实参中的左值和右值性质就无法传递到函数模板中去,所以我们必须用T&&
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
void func(int m, int &n)
{
	++n;
	cout << m + n << endl;
}
//函数模板:要把收到的参数以及这些参数相应的类型(比如左值引用还是右值应用,比如const)不变的转变给其他函数(转发给func函数),就是转发
template <typename F, typename T1, typename T2>
void func_test(F&& f, T1 &&t1, T2 &&t2) //F就是要转发到的目标函数
{
	//转发
	f(t1, t2);
}

int main()
{  
	int i = 50;
	//func(42, i);
	int j = 70;
	func_test(func, 20, j);
	cout << j << endl;
	return 0;
}

转发2的问题


#include <iostream>
#include <boost/type_index.hpp>


using namespace std;

void func(int m, int &n)
{
	++n;
	cout << m + n << endl;

}

void func_(int&& m, int& n)
{
	cout << m << " " << n << endl;
}
//函数模板:要把收到的参数以及这些参数相应的类型(比如左值引用还是右值应用,比如const)不变的转变给其他函数(转发给func函数),就是转发

template <typename F, typename T1, typename T2>
void func_test(F&& f, T1 &&t1, T2 &&t2) //F就是要转发到的目标函数
{
	f(t1, t2); //t1是左值, m是右值,所以出错
	//无法将参数 1 从“T1”转换为“int && ”

}

int main()
{  
	int i = 50;
	//func(42, i);
	int j = 70;
	func_(50, j);


	int&& r_ref = 80; //右值引用绑定右值  虽然&&是绑定到右值 但是右值引用r_ref本身是左值;
	int& l_ref = r_ref; 
	int x = 5;
	//func_(2, x);
	  
	func_test(func_, 2, x);
	return 0; 
}

完美转发

  • 可以写接受任何类型实参的函数模板,并将其转发到目标函数,目标函数会接收到与转发函数所接收到的完全相同(左值性和右值性都要保持的实参);
  • 使用std::forward来实现完美转发

std::forward

  • C++11新函数,专门为转发而存在;要么返回一个左值,要么返回一个右值;
  • 发挥作用的条件:调用函数模板,函数模板参数是万能类型,函数模板负责转发;
  • std::forward的能力就是按照参数本身的类型转发;
  • 对std::forward函数的两种理解
    • 实参如果原来是左值,到了形参中还是左值,forward是按照形参原来的类型处理,所以std::forward之后还是个左值;
    • 实参如果原来是右值,到了形参中变成了左值,forward是按照形参原来的类型处理,所以std::forward之后还是右值;
    • forward有强制把左值转成右值的能力,所以forward只是对原来是右值的情况有用;
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;

void func(int m, int &n)
{
	++n;
	cout << m + n << endl;

}
void func_(int&& m, int& n)
{
	cout << m << " " << n << endl;
}

//函数模板:要把收到的参数以及这些参数相应的类型(比如左值引用还是右值应用,比如const)不变的转变给其他函数(转发给func函数),就是转发
template <typename F, typename T1, typename T2>
void func_test(F&& f, T1 &&t1, T2 &&t2) //F就是要转发到的目标函数
{
	f(std::forward<T1>(t1), std::forward<T2>(t2)); //t1是左值, m是右值,所以出错
	//无法将参数 1 从“T1”转换为“int && ”

}

int main()
{  
	int i = 50;
	//func(42, i);
	int j = 70;
	func_(50, j);


	int&& r_ref = 80; //右值引用绑定右值  虽然&&是绑定到右值 但是右值引用r_ref本身是左值;
	int& l_ref = r_ref; 
	int x = 5;
	//func_(2, x);
	  
	func_test(func_, 2, x);
	return 0; 
}

#include <iostream>
#include <boost/type_index.hpp>
using namespace std;

void printInfo(int& t) //类型为左值引用的形参
{
	cout << "void printInfo(int& t)" << endl;
}

void printInfo(int&& t) //类型为右值引用的形参
{
	cout << "void printInfo(int&& t)" << endl;
}

template <typename T>
void Test(T&& t) //万能引用
{
	printInfo(t); //t是右值, T = int, t = int && t本身是左值
			      //t是左值, T = int &, t = int &  t本身是左值
	printInfo(std::forward<T>(t)); //t为右值
								   //t为左值
	printInfo(std::move(t)); //左值转右值
}
int main()
{  
	Test(4); //4是右值 左值引用,右值引用,右值引用
	cout << "-------------------------------" << endl;
	int i = 5;
	Test(i); //i是左值 左值引用,左值引用,右值引用
	//原来实参是左值还是右值会保存到Test里的万能引用中的T类型中去;必须是万能引用才会保存;
	return 0; 
}

std::move和std::forward的区别

std::forward是强制把一个左值转成一个右值,但是如果实参就是左值,那么forward啥也不干;有条件强制类型转换;
std::move无条件强制类型转换;

万能引用

  • 万能引用不是新的引用类型;只是一种形式;T&&
  • 万能引用的概念有意义,方便理解引用折叠;

std::forward用法补充


#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
int main()
{  
	int tmp = 12;
	int&& def = std::move(tmp);

	//std::forward <>内部是什么,就是要把tmp转换成什么类型
	int&& r_def = std::forward<int>(tmp); //forward能把左值成功转换成右值;

	return 0; 
}
posted @ 2020-08-06 10:59  NaughtyCoder  阅读(610)  评论(0)    收藏  举报