文章目录
- 1.可变模版参数
- 1.1 基本语法及其原理
- 1.2 包扩展
- 1.3 empalce系列接口
- 1.3.1 push_back和emplace_back
- 1.3.2 emplace_back在list中的使用(模拟实现)
- 2. lambda
- 2.1 lambda表达式语法
1.可变模版参数
1.1 基本语法及其原理
1. C++11支持可变参数模版,也就是说支持可变数量参数的函数模版和类模版,可变数目的参数为参数包,存在两种参数包:模板参数包,表示零或多个模板参数;函数参数包:表示零或多个函数参数。
2. 用省略号来指出一个模板参数或函数参数的表示一个包,在模板参数列表中,class…或typename…指出接下来的参数表示零或多个类型列表;
在函数参数列表中,类型名后面跟…指出接下来表示零或多个形参对象列表;
函数参数包可以用左值引用或右值引用表示,跟前面普通模板一样,每个参数实例化时遵循引用折叠规则。
3. 可变参数模板的原理跟模板类似,本质还是去实例化对应类型和个数的多个函数。
4. 可变模版参数:参数类型可变,参数个数可变
…Args:模版参数包
…args:函数参数包
// 0-N个参数
// ...:表示多个参数
template<class ...Args>
void Print(Args&& ... args)
{
// sizeof...是一个新的运算符
// sizeof... 计算参数包里面有几个参数
cout << sizeof...(args) << endl;
}
int main()
{
Print(); // 0个参数
Print(1);// 1个参数
Print(1, 2.2);// 2个参数
return 0;
}
- 可变模版参数:本质上是模版的模版,可变模版参数实例化出对应的各个模版,这些模版再实例化出对应的类或者是函数
- 总结:模版,一个函数模版可以实例化出多个不同类型参数的函数,可变参数模版,一个可变参数模版函数可以实例化出多个不同参数个数的模版函数
- 主要就是一个可变参数模版实例化出多个函数模版,函数模版再实例化多个函数
1.2 包扩展
1. 包扩展:解析出参数包的内容
2. 编译时递归包括展,其实也不是递归,因为每次都生成不同的函数重载,只是每次用自己这个函数进行传参(包扩展)
参数包的第一种扩展方式(编译时递归):
// 打印参数包内容
template <class ...Args>
void Print(Args... args)
{
// 可变参数模板是编译时解析,不是运行时解析
// 下面是运行获取和解析,所以不支持这样用
// cout << sizeof...(args) << endl;
for (size_t i = 0; i < sizeof...(args); i++)
{
cout << args[i] << " ";
}
cout << endl;
}
// 这是编译时的逻辑,不是运行时逻辑
void ShowList()
{
// 编译器递归终止的条件,参数包是0个时,直接匹配这个函数
cout << endl;
}
template<class T,class ...Args>
void ShowList(T x, Args ...args)
{
// 运行时
/*if (sizeof...(args) == 0)
return;*/
cout << x << " ";
// args是N个参数的参数包,一个参数传给x,剩下的N-1个参数
// 传给args,继续往下递归
ShowList(args...);
}
// 编译时递归推导解析参数
template<class ...Args>
void Print(Args ... args)
{
ShowList(args...);
}
int main()
{
Print();
Print(1);
Print(1, string("xxxxxx"));
Print(1,string("xxx"),2.2);
return 0;
}
模版写给编译器,让编译器生成对应的包括展
第二种扩展方式(通过函数调用):
// 下面两个GetArg都可以用
// 可以随便返回任何数
template<class T>
int GetArg(const T& x)
{
cout << x << " ";
return 0;
}
//template <class T>
//const T& GetArg(const T& x)
//{
// cout << x << " ";
// return x;
//}
template <class ...Args>
void Arguments(Args... args)
{}
template <class ...Args>
void Print(Args... args)
{
// 注意GetArg必须返回或者到的对象,这样才能组成参数包给Arguments
Arguments(GetArg(args)...);
有几个参数调用几次Arguments
}
// 实际上就是下面这段
// 本质可以理解为编译器编译时,包的扩展模式
// 将上⾯的函数模板扩展实例化为下⾯的函数
//void Print(int x, string y, double z)
//{
// Arguments(GetArg(x), GetArg(y), GetArg(z));
//}
int main()
{
Print(1, string("xxxxx"), 2.2);
return 0;
}
1.3 empalce系列接口
1.3.1 push_back和emplace_back
- 传左值:都会走拷贝构造
- 传右值:都走移动构造
- 对于深拷贝:
直接传参,push_back,类模版实例化出string,会构造临时对象+移动构造
emplace_back,会直接用const char* 构造
所以emplace_back会稍微快一点
对于浅拷贝:
比如Date,字节大小不大,push_back变为构造 + 拷贝构造,emplace_back还是直接构造
多参数的: - 对于pair键值对,It1.emplace_back({“苹果”,1})是不支持的,因为emplace_back支持传多个参数类型,而不知道你传的是键值对pair
1.3.2 emplace_back在list中的使用(模拟实现)
其实并不都是要像Print中解析包扩展,这样往下传即可
// 初始化列表
list_node() = default;
template <class... Args>
list_node(Args&&... args)
: _next(nullptr)
, _prev(nullptr)
, _data(std::forward<Args>(args)...)
{}
// emplace_back()
template <class... Args>
void emplace_back(Args&&... args)
{
insert(end(), std::forward<Args>(args)...);
}
// insert()
template <class... Args>
iterator insert(iterator pos, Args&&... args)
{
Node* cur = pos._node;
Node* newnode = new Node(std::forward<Args>(args)...);
Node* prev = cur->_prev;
// prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
return iterator(newnode);
}
2. lambda
2.1 lambda表达式语法
1. lambda 表达式本质是一个匿名函数对象,跟普通函数不同的是他可以定义在函数内部,也可以写在全局,lambda 表达式语法使用层而言没有类型,所以我们一般是用auto或者模板参数定义的对象去接收 lambda 对象。
2.lambda表达式的格式:
[捕捉列表](参数列表)->返回值类型{函数体}
3. lambda可以传给模版的参数,也可以传给auto,让auto自动推导
#include<algorithm>
struct Goods
{
string _name; // 名字
double _price; // 价格
int _evaluate; // 评价
// ...
Goods(const char* str, double price, int evaluate)
:_name(str)
, _price(price)
, _evaluate(evaluate)
{}
};
// 仿函数,价格升序
struct Compare1
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price < gr._price;
}
};
// 价格降序
struct Compare2
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price > gr._price;
}
};
int main()
{
vector<Goods> v = { { "苹果", 2.1, 5 }, { "⾹蕉", 3, 4 }, { "橙⼦", 2.2, 3}, { "菠萝", 1.5, 4 } };
// 类似这样的场景,我们实现仿函数对象或者函数指针⽀持商品中
// 不同项的⽐较,相对还是⽐较⿇烦的,因为只能比较一项,那么这⾥lambda就很好⽤了
// 写法一
// sort(v.begin(), v.end(), Compare1());
// sort(v.begin(), v.end(), Compare2());
// 写法二
// 用 lambda就可以用匿名函数对象比较多种数据,不用写专门的仿函数单独比较一项了
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)
{return g1._price < g2._price;});
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)
{return g1._price > g2._price;});
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)
{return g1._evaluate < g2._evaluate;});
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)
{return g1._evaluate > g2._evaluate;});
// [](const Goods& g1, const Goods& g2) {return g1._evaluate > g2._evaluate; }
// 匿名的函数对象
return 0;
}