地方旅游网站建设方案,成都网站建设有名的,wordpress 链接 中文乱码,网站优化公司服务目录
可变参数模板 lambda表达式
lambda表达式语法 捕获列表说明 可变参数模板 可变参数#xff1a;可以有0到n个参数#xff0c;如之前学过的 Printf C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板
模板参数包
// Args是一个模板参数包可以有0到n个参数如之前学过的 Printf C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板
模板参数包
// Args是一个模板参数包args是一个函数形参参数包
// 声明一个参数包Args...args这个参数包中可以包含0到任意个模板参数。
template class ...Args
void ShowList(Args... args)
{}
上面的参数args前面有省略号所以它就是一个可变模版参数我们把带省略号的参数称为“参数 包”它里面包含了0到NN0个模版参数。我们无法直接获取参数包args中的每个参数的 只能通过展开参数包的方式来获取参数包中的每个参数这是使用可变模版参数的一个主要特 点也是最大的难点即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变 参数所以我们的用一些奇招来一一获取参数包的值
传参时参数个数没有限制 当没有val参数时如何查看参数包内容
逗号表达式展开参数包
这种展开参数包的方式不需要通过递归终止函数是直接在expand函数体中展开的, printarg 不是一个递归终止函数只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式 实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。 expand函数中的逗号表达式(printarg(args), 0)也是按照这个执行顺序先执行 printarg(args)再得到逗号表达式的结果0。同时还用到了C11的另外一个特性——初始化列 表通过初始化列表来初始化一个变长数组, {(printarg(args), 0)...}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... )最终会创建一个元素值都为0的数组int arr[sizeof... (Args)]。由于是逗号表达式在创建数组的过程中会先执行逗号表达式前面的部分printarg(args) 打印出参数也就是说在构造int数组的过程中就将参数包展开了这个数组的目的纯粹是为了在 数组构造的过程展开参数包
也可以这样 vector中有可变参数包 push_back不能像红框那样传参必须make_pair
区别push_back要先构造pair对象构造拷贝构造/移动构造emplace_back和push_back一样
而对于listpush_back和emplace_back不一样emplace_back没有拷贝构造 写一个日期类 g下vector的push_back和emplace_back仍然没有区别而list仍然有区别 lambda表达式 像函数一样使用的对象/类型
1.函数指针 2.仿函数/函数对象 3.lambda
如果待排序元素为自定义类型需要用户定义排序时的比较规则
struct Goods
{
string _name; // 名字
double _price; // 价格
int _evaluate; // 评价
Goods(const char* str, double price, int evaluate)
:_name(str)
, _price(price)
, _evaluate(evaluate)
{}
};
struct ComparePriceLess
{
bool operator()(const Goods gl, const Goods gr)
{
return gl._price gr._price;
}
};
struct ComparePriceGreater
{
bool operator()(const Goods gl, const Goods gr)
{
return gl._price gr._price;
}
};
int main()
{
vectorGoods v { { 苹果, 2.1, 5 }, { 香蕉, 3, 4 }, { 橙子, 2.2,
3 }, { 菠萝, 1.5, 4 } };
sort(v.begin(), v.end(), ComparePriceLess());
sort(v.begin(), v.end(), ComparePriceGreater());
}
上面这种排序比较麻烦需要写多个排序函数 用lambda对上面的排序进行优化
lambda表达式语法 lambda表达式书写格式[capture-list] (parameters) mutable - return-type { statement } 1. lambda表达式各部分说明 [capture-list] : 捕捉列表该列表总是出现在lambda函数的开始位置编译器根据[]来 判断接下来的代码是否为lambda函数捕捉列表能够捕捉上下文中的变量供lambda 函数使用。 (parameters)参数列表。与普通函数的参数列表一致如果不需要参数传递则可以 连同()一起省略 mutable默认情况下lambda函数总是一个const函数mutable可以取消其常量 性。使用该修饰符时参数列表不可省略(即使参数为空)。
-returntype返回值类型。用追踪返回类型形式声明函数的返回值类型没有返回 值时此部分可省略。返回值类型明确情况下也可省略由编译器对返回类型进行推 导。 {statement}函数体。在该函数体内除了可以使用其参数外还可以使用所有捕获 到的变量。 注意 在lambda函数定义中参数列表和返回值类型都是可选部分而捕捉列表和函数体可以为 空。因此C11中最简单的lambda函数为[]{}; 该lambda函数不能做任何事情。 {}里写的语句较多 如果不传参数进行x和y交换使用捕捉列表但是默认捕捉的对象不能修改 加了moutable之后程序能运行但是结果不对 捕获列表说明 捕捉列表描述了上下文中那些数据可以被lambda使用以及使用的方式传值还是传引用。 [var]表示值传递方式捕捉变量var []表示值传递方式捕获所有父作用域中的变量(包括this) [var]表示引用传递捕捉变量var []表示引用传递捕捉所有父作用域中的变量(包括this) [this]表示值传递方式捕捉当前的this指针
注意 a. 父作用域指包含lambda函数的语句块 b. 语法上捕捉列表可由多个捕捉项组成并以逗号分割。 比如[, a, b]以引用传递的方式捕捉变量a和b值传递方式捕捉其他所有变量 [a, this]值传递方式捕捉变量a和this引用方式捕捉其他变量 c. 捕捉列表不允许变量重复传递否则就会导致编译错误。 比如[, a]已经以值传递方式捕捉了所有变量捕捉a重复 d. 在块作用域以外的lambda函数捕捉列表必须为空。 e. 在块作用域中的lambda函数仅能捕捉父作用域中局部变量捕捉任何非此作用域或者 非局部变量都 会导致编译报错。 f. lambda表达式之间不能相互赋值即使看起来类型相同 此时结果正确
之前的问题就能得到解决就不需要些那么多的比较函数 这种情况什么都不打印 这是匿名函数对象 lambda的定义而我们没有调用它所以什么都不打印 不允许这种写法上面有解释过 c. 捕捉列表不允许变量重复传递否则就会导致编译错误。 比如[, a]已经以值传递方式捕捉了所有变量捕捉a重复
这种写法也会报错其余都是引用捕捉a传值捕捉捕捉后进行a会报错 但这样可以不对a进行操作 lambda父作用域父作用域是当前所在的函数栈帧 f不在当前栈帧但能编译成功。这是因为全局变量不在栈帧中在静态区哪个位置都可以对他进行使用 当我们把abcde写到其它函数进行捕捉此时会报错 捕捉列表的本质就是在传参
lambda底层实际是一个仿函数
class Rate
{
public:
Rate(double rate): _rate(rate)
{}
double operator()(double money, int year)
{ return money * _rate * year;}
private:
double _rate;
};
int main()
{
// 函数对象
double rate 0.49;
Rate r1(rate);
r1(10000, 2);
// lamber
auto r2 [](double monty, int year)-double{return monty*rate*year;
};
r2(10000, 2);
return 0;
} 从使用方式上来看函数对象与lambda表达式完全一样。 函数对象将rate作为其成员变量在定义对象时给出初始值即可lambda表达式通过捕获列表可 以直接将该变量捕获到。 实际在底层编译器对于lambda表达式的处理方式完全就是按照函数对象的方式处理的即如 果定义了一个lambda表达式编译器会自动生成一个类在该类中重载了operator()。 lambda表达式之间不能相互赋值即使看起来类型相同
不能互相赋值是因为这是俩个lambda俩个lambda的uuid上面汇编lambda:后面的字符串不一样,相当于这俩个lambda属于俩个类型但是可以将lambda表达式赋给函数指针。
void (*PF)();
int main()
{
auto f1 []{cout hello world endl; };
auto f2 []{cout hello world endl; };
//f1 f2; // 编译失败---提示找不到operator()
// 允许使用一个lambda表达式拷贝构造一个新的副本
auto f3(f2);
f3();
// 可以将lambda表达式赋值给相同类型的函数指针
PF f2;
PF();
return 0;
}