网站建设规划书怎么写,重庆响应式网站多少钱,国内网站做得好的公司,济南软件开发定制#x1f493;博主CSDN主页:杭电码农-NEO#x1f493; ⏩专栏分类:C从入门到精通⏪ #x1f69a;代码仓库:NEO的学习日记#x1f69a; #x1f339;关注我#x1faf5;带你学习C #x1f51d;#x1f51d; C11 1. 前言2. lambda表达式的提出3. lambda表达… 博主CSDN主页:杭电码农-NEO ⏩专栏分类:C从入门到精通⏪ 代码仓库:NEO的学习日记 关注我带你学习C C11 1. 前言2. lambda表达式的提出3. lambda表达式的语法4. lambda表达式的捕捉列表5. function包装器6. function包装器使用场景7. decltype关键字用法8. 可变参数模板讲解9. 总结 1. 前言
C11新增了lambda表达式来解决 特定场景下使用仿函数很麻烦的问题 而function包装器则将C语言中复杂的 函数指针问题给简单化了!
本章重点: 本篇文章着重讲解lambda表达式 的语法使用方法和实用场景以及 function包装器的语法使用以及如何 用包装器一次性搞定函数指针,仿函数 和lambda表达式,最后简单讲解关键字 decltype的使用方法和可变模板参数 2. lambda表达式的提出
在C98中,对自定义类型进行排序时, 需要自己写仿函数,并传递给sort库函数 但是如果每次要按照自定义类型的不同 成员变量进行排序的话,就要写很多个仿 函数,十分的不方便,C11给出了一个新玩法:
struct Goods
{
string _name; // 名字
double _price; // 价格
int _evaluate; // 评价
Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate)
{}
};
vectorGoods v { { 苹果, 2.1, 5 }, { 香蕉, 3, 4 }, { 橙子, 2.2, 3 }, { 菠萝, 1.5, 4 } };
sort(v.begin(), v.end(), [](Goods g1, Goods g2)-bool
{return g1._price g2._price; });//按照价格升序
sort(v.begin(), v.end(), [](Goods g1, Goods g2)-bool
{return g1._price g2._price; });//按照价格降序
sort(v.begin(), v.end(), [](Goods g1, Goods g2)-bool
{return g1._evaluate g2._evaluate; });//按照评价升序
sort(v.begin(), v.end(), [](Goods g1, Goods g2)-bool
{return g1._evaluate g2._evaluate; });//按照评价降序看不懂没关系,现在你只需要知道这种 写法可以代替完美去写仿函数即可.
它的大概意思请看下图: 3. lambda表达式的语法
书写格式:
A捕捉列表, B参数列表, C返回值 [A] (B)- C {函数体}
lambda表达式各部分说明: 捕捉列表总是出现在lambda函数的开始位置编译器根据[]来判断接下来的代码是否为lambda表达式捕捉列表能够捕捉上下文中的变量供lambda函数使用 参数列表,与普通函数的参数列表一致,如果不需要传递参数,则可以连同()一起省略 返回值类型,没有返回值时此部分可省略,返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。 函数体,在该函数体内,除了可以使用其参数外,还能使用捕捉列表中的变量 参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空,因此C11中最简单的lambda表达式为[]{};,该lambda表达式不能做任何事情。 事实上,可以把lambda表达式看作一个 class类,被捕捉或定义的变量可以看作 是类中的成员变量,但是lambda表达式 有一个特性是它默认有const属性,相当于 这个类的所有成员变量都是const修饰的 ,无法被修改,要想修改它,要在返回值前加 上一个关键字: mutable 利用lambda表达式实现一个swap函数:
int x 3;
int y 5;
auto myswap [](int x,inty)mutable-void
{int tmp x;x y;y tmp;
};
myswap(x,y);实际上lambda表达式的返回值是一个函数对象 在sort中传参正是要一个函数对象,而在这里需要 的是用函数对象来充当一个函数,也就是使用(x,y) 4. lambda表达式的捕捉列表
lambda表达式的捕捉列表[ ]可以 捕捉父作用域的变量供自己使用
下面请看它的捕捉规则:
首先捕捉分为值捕捉和引用捕捉 int a 10;
char* b xxxxxxxxxxx
vectordouble v{1.11,2.22};
auto it [a,b,c]()-bool{return babcd;};
//以值传递的方式捕捉a和c,引用捕捉b其实可以发现,lambda表达式的使用 方法和仿函数及其相似实际在底层 编译器对于lambda表达式的处理方式 完全就是按照函数对象的方式处理的 即如果定义了一个lambda表达式 编译器会自动生成一个类 在该类中重载了operator() 5. function包装器
C中的function本质是一个类模板 也是一个包装器,请看下面的代码:
ret func(x);
// 上面func可能是什么呢那么func可能是函数名函数指针函数对象(仿函数对象)
//也有可能是lamber表达式对象所以这些都是可调用的类型如此丰富的类型
//可能会导致模板的效率低下
templateclass F, class T
T useF(F f, T x)
{static int count 0;cout count: count endl;cout count: count endl;return f(x);
}
double f(double i)
{return i / 2;
}
struct Functor
{double operator()(double d){return d / 3;}
};
int main()
{
// 函数名cout useF(f, 11.11) endl;// 函数对象cout useF(Functor(), 11.11) endl;// lamber表达式cout useF([](double d)-double{ return d/4; }, 11.11) endl;return 0;
}我们会发现useF函数模板实例化了三份 但是不管func是什么东西,都可以用 function来定义,这也就提高了效率
function包装器的使用方法: 第一个int代表返回值类型 括号里面用逗号分割的是参数类型 6. function包装器使用场景
function的使用场景非常多,博主 结合了自己学习操作系统的编码 经验来给大家做几个分享:
创建线程时用function: 在Linux下创建线程时,我们使用 pthread_create函数时要传入此 线程要调用的线程函数对象,这里 配合function使用起来非常方便 pthread_t tid;
pthread_create(tid,nullptr,[](void* args)-void*
{//函数体
},nullptr);线程池内部的处理方法用function 在编写线程池时,每一个线程被创建 出来可能会执行不同的任务,也就是 执行不同的函数,但所有函数的参数 与返回值都一样,这是就可以使用一个 数组保存函数方法,而数组中的元素 类型就是function定义的对象类型! //func_t是一种函数类型,此类型的函数的返回值和参数都是int
typedef functionint(int,int) func_t;
//将不同的函数方法插入到数组中,使用时去数组找!
vectorfunc_t Task;这里旨在告诉大家,function的使用场景 很多,即使你现在还没有接触过它,你也 应该掌握它!!! 7. decltype关键字用法
关键字decltype可以将变量的 类型声明为表达式指定的类型
使用场景以及用法:
// decltype的一些使用使用场景
templateclass T1, class T2
void F(T1 t1, T2 t2)
{decltype(t1 * t2) ret;cout typeid(ret).name() endl;
}
int main()
{
const int x 1;
double y 2.2;
decltype(x * y) ret; // ret的类型是double
decltype(x) p; // p的类型是int*
cout typeid(ret).name() endl;
cout typeid(p).name() endl;
F(1, a);
return 0;
}你可能会觉得decltype关键字很鸡肋 因为有auto可以自动推导类型了,还要 decltype干啥?不错!auto固然好用,但是 有些场景下你想要一个具体的类型时, 比如vector的元素类型时,你不能用auto decltype([](int x)-int{return 2*x10;}) it;8. 可变参数模板讲解
下面是一个基本可变参数的函数模板
// Args是一个模板参数包args是一个函数形参参数包
// 声明一个参数包Args...args这个参数包中可以包含0到任意个模板参数。
template class ...Args
void ShowList(Args... args)
{}上面的参数args前面有省略号所以它就是一个可变模版参数我们把带省略号的参数称为“参数包”它里面包含0~NN0个模版参数。我们无法直接获取参数包args中的每个参数的只能通过展开参数包的方式来获取参数包中的每个参数这是使用可变模版参数的一个主要特点也是最大的难点即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变参数所以我们的用一些奇招来一一获取参数包的值 递归函数的方式展开参数包:
// 递归终止函数
template class T
void ShowList(const T t)
{cout t endl;
}
// 展开函数
template class T, class ...Args
void ShowList(T value, Args... args)
{cout value ;ShowList(args...);
}
int main()
{ShowList(1);ShowList(1, A);ShowList(1, A, std::string(sort));return 0;
}对于可变模板参数的认知到这儿就差不多了 这属于是了解认知的范畴,下次看见了不会懵逼 9. 总结
本篇文章介绍了两个C11十分 常用的内容,lambda表达式和包装器 function是需要同学们掌握并且能熟练 编写的,后面的decltype和可变模板参数 属于了解内容,保证你下次看见这个的 时候不会懵逼 下期预告:智能指针详解