凡科建站源码,云浮哪有做网站公司,wordpress添加路由,做网站要用到哪些架包目录
1、引言
2、仿函数
3、lambda表达式
3.1、lambda表达式的一般形式
3.2、返回类型说明
3.3、捕获列表的规则
3.4、可以捕获哪些变量
3.5、lambda表达式给编程带来的便利 VC常用功能开发汇总#xff08;专栏文章列表#xff0c;欢迎订阅#xff0c;持续更新...常用功能开发汇总专栏文章列表欢迎订阅持续更新...https://blog.csdn.net/chenlycly/article/details/124272585C软件异常排查从入门到精通系列教程专栏文章列表欢迎订阅持续更新...https://blog.csdn.net/chenlycly/article/details/125529931C软件分析工具从入门到精通案例集锦专栏文章正在更新中...https://blog.csdn.net/chenlycly/article/details/131405795C/C基础与进阶专栏文章持续更新中...https://blog.csdn.net/chenlycly/category_11931267.html C11新特性很重要作为C开发人员很有必要去学习不仅笔试面试时会涉及到开源代码中也在大规模的使用。以很多视频会议及直播软件都在使用的开源WebRTC项目为例WebRTC代码中大篇幅地使用了C11及以上的新特性要读懂其源码必须要了解这些C的新特性。所以接下来一段时间我将结合工作实践给大家详细讲解一下C11的新特性以供借鉴或参考。
1、引言 在C中我们可以使用函数名、函数指针、仿函数去实现函数的调用。C11引入了lambda表达式又称匿名函数给我们引入了一种新的函数调用方式给我们编程带来了很大的便利。今天我们来讲讲仿函数和lambda表达式。
2、仿函数 仿函数是一种特殊的类或结构体不是C11引入的之前就有了。它重载了函数调用运算符operator()并且可以像函数一样被调用同时它也可以拥有自己的数据成员和成员函数。仿函数通常用于算法如sort、find、transform等和容器如set、map、list等中以提供自定义的操作行为。在C11中可以使用lambda表达式实现简单的仿函数。 下面是个类中重载 operator()的实例
class MyFunctor
{
public:MyFunctor(int tmp) : round(tmp) {}int operator()(int tmp) { return tmp round; }
private:int round;
};int main()
{int round 2;MyFunctor f(round); //调用构造函数cout result f(1) endl; // operator()(int tmp)return 0;
}
通过类对象去调用重载的operator()方法。 在C里我们通过在一个类中重载operator()运算符的方法去使用一个函数对象而不是一个普通函数。 再看一个比较数大小的实例
class compare_class
{public:bool operator() (int A, int B) const{return A B;}
};// Declaration of C sorting function.
templateclass ComparisonFunctor
void sort_ints(int* begin_items, int num_items, ComparisonFunctor c);int main()
{int items[]{4, 3, 1, 2};compare_class functor;sort_ints( items, sizeof(items)/sizeof(items[0]), functor);
}
3、lambda表达式 lambda表达式又称匿名函数是一个可调用的代码单元可以理解为一个未命名匿名的内联函数。与一般的函数类似lambda表示式有一个返回类型、一个参数列表和一个函数体。但和函数不同的是lambda表达式是直接定义在函数内部。 lambda表达式的引入给我们编程带来了很大的便利可以大大简化我们的编码
3.1、lambda表达式的一般形式
lambda表达式的一般形式如下 [capture list]( parameter list ) - return type { function body } 其中 1capture list捕获列表是一个本表达式所在函数的局部变量的列表 2parameter list参数列表是给本lambda表达式传入的参数列表 3return type返回类型是本lambda表达式的返回值类型可省略 4function body函数体是本lambda表达式的内部实现。 对于普通函数返回类型位于函数开始处但lambda表达式因为其形式返回类型不能放在开始处必须使用尾置的方式来指定返回类型。我们可以忽略参数列表和返回值类型但必须包含捕获列表和函数体比如
auto f [] { return 20; };
3.2、返回类型说明 直接在lambda表达式中指定返回类型没什么问题。下面我们看看没指定返回类型时将会发生什么。 如果lambda表达式的函数体中只包含一个return语句如果没指定lambda表达式的返回值则编译时会根据return语句中的内容推导出本表达式的返回值类型比如实现两个整型数据相加的lambda表达式
[] ( int a, int b) { return a b; }
本lambda中没有指定函数返回类型根据表达式ab可以推断出本lambda返回值类型为int。 如果lambda表示式中不是只包含单一的return语句多条语句编译时编译器认定该lambda返回值类型为void。比如返回一个数绝对值的lambda如下
[] ( int a ) { if ( a 0) return -a; else return a; };
因为包含了多条语句所以编译器认定该lambda返回void编译时会报错因为返回类型为void却使用return返回了int类型不一致了所以报错
3.3、捕获列表的规则 捕获列表涉及到访问所在函数哪些局部变量以及访问变量的方式。访问变量的方式主要有以引用的方式访问还是以值的方式访问。如果是将lambda要访问的变量在捕获列表中罗列出来则是显示捕获如果不罗列就是隐式捕获。 下面给出完整的捕获列表规则
捕获类型描述[]空捕获列表。lamda表达式内部不使用所在函数中的变量。只有捕获列表不为空才能使用所在函数中的变量。[names]names是一个逗号分割的名字列表这些名字是lamda所在函数的局部变量的名称。默认情况下捕获列表中的变量值被拷贝变量前面可以添加加则表示采用引用捕获方式。[]隐式捕获列表让编译器根据lamda内部的代码去推断使用了所在函数的哪些局部变量。采用引用捕获方式lamda体中所使用的来自所在函数的变量都采用引用方式使用。[]隐式捕获列表让编译器根据lamda内部的代码去推断使用了所在函数的哪些局部变量。采用值捕获方式lamda体中将拷贝所使用的来自所在函数的变量的值。[, identifier_list] identifier_list理解为不使用引用捕获的特例是逗号分割的列表包含0个或多个来自所在函数的变量这些变量采用值捕获identifier_list列表中的名字前面不能加。而除identifier_list列表中指明的变量之外的任何隐式捕获的变量都采用引用捕获方式。[, identifier_list]identifier_listt理解为不使用值捕获的特例是逗号分割的列表包含0个或多个来自所在函数的变量这些变量采用引用捕获identifier_list列表中的名字前面必须使用。而除identifier_list列表中指明的变量之外任何隐式捕获的变量都采用值捕获方式。
关于捕获列表的例子如下
int main()
{int a 0, b 1;auto f1 []{ return a; }; // error, 没有捕获外部变量auto f2 []{ return a; }; // ok, 值传递方式捕获所有外部变量auto f3 []{ return a; }; // error, a是以赋值方式捕获的无法修改auto f4 []() mutable { return a; }; // ok, 加上mutable修饰符后可以修改按值传递进来的拷贝auto f5 []{ return a; }; // ok, 引用传递方式捕获所有外部变量, 并对a执行自加运算auto f6 [a]{ return ab; }; // error, 没有捕获变量bauto f9 [a,b]{ return a(b); }; // ok, 捕获a, bauto f8 [,b]{ return a(b); }; // ok, 捕获所有外部变量bauto f9 [,b]{ return a(b); }; // error, 捕获所有外部变量不能使用bb是特例和默认的引用捕获不一样使用值捕获所以不能加auto f10 [,b]{ return a(b); }; // error, 捕获所有外部变量不能使用bb是特例和默认的值引用不一样使用引用捕获前面必须加return 0;
}
3.4、可以捕获哪些变量 一般lambda表达式是放置在一个函数中的即在函数中嵌入的lambda函数实现体中可以访问其所在函数的局部变量。如果lambda表达式所在函数是一个类的成员函数则lambda内部也可以访问当前函数所在的类的成员变量这一点可能很多人不知道。比如
class Test
{
public:int i 0; // 类的成员变量void func(int x, int y){auto x1 []{ return i; }; // error, 没有捕获外部变量auto x2 []{ return ixy; }; // ok, 值传递方式捕获所有外部变量auto x3 []{ return ixy; }; // ok, 引用传递方式捕获所有外部变量auto x4 [this]{ return i; }; // ok, 捕获this指针auto x5 [this]{ return ixy; }; // error, 没有捕获x, yauto x6 [this, x, y]{ return ixy; };// ok, 捕获this指针, x, yauto x9 [this]{ return i; }; // ok, 捕获this指针, 并修改成员的值}
};
3.5、lambda表达式给编程带来的便利 lambda表达式的引入给我们编程带来了很大的便利可以大大简化我的编码。比如我们在使用STL容器的find_if、count_if、sort等算法函数时我们要传入条件函数或者比较函数这些函数直接用lambda表达式去实现要方便很多。 在以前没有lambda表达式时这些条件函数和比较函数需要在函数外实现要不定义成全局函数要不定义成静态函数甚至还要定义一些辅助的全部或者静态变量。比如有一个存放设备信息的结构体然后有个存放设备信息的vector列表给该列表简单的初始化一下
// 设备信息结构体
typedef struct tagDeviceInfo
{char szDeviceId[64]; // 设备idchar szDeviceName[64]; // 设备名称int nDevType; // 设备类型public:tagDeviceInfo(){ memset(this, 0, sizeof(tagDeviceInfo)); }
}TDeviceInfo;// 设备管理类
class CDeviceManage
{CDeviceManage();~CDeviceManage();void InitDeviceList();private:vectorTDeviceInfo m_vtDevList;
}// 初始化设备列表仅用于测试随意初始化了一些数据
void CDeviceManage::InitDeviceList()
{for ( int i 0; i 10; i ){TDeviceInfo tDevInfo;char szBuf[128] { 0 };sprintf(szBuf, E40CF3E4-CC2B-437F-A4B9-65F2D5BD071%d, i);strcpy(tDevInfo.szDeviceId, szBuf);CUIString strName;sprintf(szBuf, 设备%d, i);strcpy(tDevInfo.szDeviceName, szBuf);m_vtDevList;.push_back(tDevInfo);}
}
假设我们在CDeviceManage::FindTest成员函数中调用STL的算法函数find_if到m_vtDevList列表中搜索设备GuidszDeviceId为E40CF3E4-CC2B-437F-A4B9-65F2D5BD0715的设备信息。如果不使用lambda表达式则要将条件函数实现在CDeviceManage类外部定义成全局函数同时要将存放目标id的变量定义成全局的如下所示
char* s_lpszTargetDevId ; // 定义成静态变量// 将条件匹配函数定义在类CDeviceManage外部定义成全局函数
BOOL MatchFunc(TDeviceInfo tDevInfo)
{return strcmp( s_lpszTargetDevIdtDevInfo.szDeviceId ) 0;
}bool CDeviceManage::FindTest()
{// 对静态变量s_pTargetDevId进行赋值s_lpszTargetDevId E40CF3E4-CC2B-437F-A4B9-65F2D5BD0715;vectorTDeviceInfo::iterator itor std::find_if(m_vtDevList.begin(), m_vtDevList.end(), MatchFunc);if itor ! m_vtDevList.end() {// 找到对应的设备信息进行后续处理的代码省略// ......return true;}return false;
}
上述代码实现的相对麻烦一些。 如果使用lambda表达式代码要简洁很多不用将条件匹配函数定义成全局函数也不用定义静态辅助变量s_lpszTargetDevId用lambda表达式实现的代码如下
bool CDeviceManage::FindTest()
{char* lpszTargetDevId E40CF3E4-CC2B-437F-A4B9-65F2D5BD0715;vectorTDeviceInfo::iterator itor std::find_if(vtDevList.begin(), vtDevList.end(), [](const TDeviceInfo tDevInfo){return strcmp(lpszTargetDevId, tDevInfo.achDeviceId) 0; } );// 后续代码省略// ......
} 至于为什么要使用STL的算法函数去搜索 因为STL的算法函数的效率比较高比直接去for循环遍历效率会高很多如果STL列表中存放了大量的数据数据搜索就要讲究效率了就要使用到STL算法函数比直接for循环变量要高上几个数量级这点在项目中对比测试过关于使用STL算法函数提高搜索效率的文章可以参见我之前写的文章
VC调用STL算法函数有效提升STL列表的搜索速度附源码https://blog.csdn.net/chenlycly/article/details/123943134VC如何使用C STL标准模板库中的算法函数附源码https://blog.csdn.net/chenlycly/article/details/125486409