左值右值引用,完美转发

1.c++98/03,类模板和函数模板只能含固定数量的模板参数,c++11的新特性可以创建接受可变参数的函数模板和类模板

//Args是一个模板参数包,args是一个函数形参参数包
//声明一个参数包Args… args,这个参数包可以包括0到任意个模板参数
template<class …Args>
void showlist(Args… args)
由于args前面有省略号,它就是一个可变模板参数,把带有省略号的参数称为参数包,它里面包含着0到N个模板参数,我们是无法直接获取参数包args,只能通过展开参数包的方式获取参数包的每个参数,语法不支持args[i]的方式获取可变参数
获取参数包的方式
1.递归函数方式展开函数包

//编译的递归推演
//第一个模板参数依次解析获取参数值
void _showlist() {
	cout << "没有模板参数" << endl;
}
template<class T>
void _showlist(const T& val) {
	cout << val << "只有一个模板参数" << " ";
}
template<class T, class ...Args>
void _showlist(const T& val, Args...args) {
	cout << val << endl;
	_showlist(args...);
}
template<class ...Args>
void showlist(Args...args) {
	_showlist(args...);
}
int main() {
	showlist();
	showlist(1);
	showlist(1, 2, 3);
	showlist(1, "ss", 2.2);
}

在这里插入图片描述
例如我们showlist(“Hello”,666,‘A’,3.1415926);
其中的模板参数包包含类型参数[const char*, int, char, double],函数参数包包含值参数[“Hello”, 666, ‘A’, 3.1415926]
在调用可变参数版本时:
“Hello"被传给形参T,剩余的参数: 666, ‘A’, 3.1415926。 然后,类型参数: int, char, double将被放入模板参数包Args中。值参数: 666, ‘A’, 3.1415926将被放入函数参数包args中。打印value的值"Hello”,开始下一轮递归。666被传给形参T,剩余的参数: ‘A’, 3.1415926。 然后,类型参数: char, double将被放入模板参数包Args中。值参数: ‘A’, 3.1415926将被放入函数参数包args中。打印T的值666,开始下一轮递归’A’被传给形参T,剩余的参数:3.1415926。 然后,类型参数: double将被放入模板参数包Args中。值参数: 3.1415926将被放入函数参数包args中。打印value的值’A’,开始下一轮递归。此时,参数包中只有一个参数,所以将调用只有一个参数版本的Print(), 3.1415926被传给形参arg,打印arg的值3.1415926,结束递归。

要初始化arr,强行让解析参数包,参数包有几个参数,printArg就一次推演生成几个

template<class T>
int  print(T val) {
	cout << val << " ";
	return 0;
}
template<class ...Args>
void showlist(Args...args) {
	int arr[] = {print(args)...};
	cout << endl;
}
int main() {
	showlist(1, 2, "1233", 'a');
}

emplace_back

template<class... Args>
void emplace_back(Args&&.. args){
Node*newnode=new Node(args...);
}
template<class... Args>
list_node(Args...args)
:data(args...)//例如,list中为pair,用参数包直接调用pair的构造
,next(nullptr)
,prev(nullptr)
{}

//直接把参数包不断往下传,直接构造到节点中的val上

emplace_back在实现时,则是在容器尾部创建这个元素,省去了拷贝或移动元素的过程
push_back首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中
2.左值和右值
一般认为,可以放在=左边的,或者能够取地址的,称为左值,只能放在等号右边的,或者不能取地址的,称为右值
常见右值 `

 10//字面常量
 i+j//表达式返回值 
 fmin(i,j)//函数调用返回值`

c++11对右值进行了区分
①.纯右值 a+b,100
②.将亡值 表达式的中间结果,函数按照值的方式进行返回
左值引用常见场景
①.引用传参 void func(const T&x)
②.引用返回 T&func() 出了函数作用域,对象生命周期还未结束
移动语义的出现
移动语义:将一个对象中的资源移动到另一个对象中的方式

为了避免 构造临时对象,深拷贝进行拷贝构造,再构造的重复流程,使用移动构造,构造临时对象通过移动构造将资源转移到临时对象中,而临时对象本身又是右值(将亡值),构造的时候会将临时对象的资源转移到要构造的对象中

要注意,右值引用本身的属性是左值

   double&& r = 1.1 + 2.2;
   double& c = r;

3.完美转发

template<tyname T>
void PerfectForward(T&&t){
Func(forward<T>(t));
}

比如PerfectForward(10),我们想输出右值引用,但由于右值引用的属性是左值

perfectForward为转发的模板函数,Func为实际目标函数,完美转发是目标函数总希望将参数按照传递给转发函数的实际类型转给目标函数,而不产生额外的开销,就好像转发者不存在一样
所谓完美:函数模板在向其它函数传递形参的时候,如果相应实参是左值,它就应该被转发为左值,如果相应实参是右值,它就应该被转发为右值。

#include<bits/stdc++.h>
using namespace std;
void func(int&x){
	cout<<"左值引用"<<endl;
}
void func(int&&x){
	cout<<"右值引用"<<endl; 
}
void func(const int &x){
	cout<<"const 左值引用"<<endl; 
}
void func(const int&&x){
	cout<<"const 右值引用"<<endl;
}
template<typename T>
void perfectForward(T&&t){
	func(forward<T>(t));
}
int main(){
	int c;
	perfectForward(3);
}

在这里插入图片描述
针对移动构造函数和移动赋值重载
如果你没有自己实现移动构造函数,且没有实现析构函数,拷贝构造函数,拷贝赋值重载中的任意一个(三个都没有实现),那么编译器就会默认生成一个移动构造。默认的移动构造函数,对于内置类型成员会逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造
如果你没有自己实现移动赋值重载,且没有实现析构函数,拷贝构造函数,拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动赋值重载函数,对于内置类型成员会逐成员按字节拷贝,对于自定义类型成员,则需要看这个成员是否实现了移动赋值重载,如果实现了就调用移动赋值重载,没有实现就调用拷贝赋值重载

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值