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;
完美转发
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);
}