app网站如何做推广方案,代理网络下载,三河网站seo,中国建设银行网站-个人客目录
一、lambda表达式
1.引入
2、lambda表达式语法
二、包装器---function
1.引入
2.包装器介绍
三、bind 一、lambda表达式
1.引入
class Person {
public:Person(int age,string name):_age(age),_name(name){}
//private://方便后面的举例int _age;string _name…目录
一、lambda表达式
1.引入
2、lambda表达式语法
二、包装器---function
1.引入
2.包装器介绍
三、bind 一、lambda表达式
1.引入
class Person {
public:Person(int age,string name):_age(age),_name(name){}
//private://方便后面的举例int _age;string _name;
};int main()
{vectorPersonv { {10,zhangsan},{40,wangwu},{20,lisi} };//当我们要通过姓名/年龄排序时我们应该怎么办//sort(v.begin(),v.end());return 0;
}
根据我们之前学过的知识我们可以写两个仿函数分别对应姓名和年龄的比较如下
struct comp_age {bool operator()(const Person x, const Person y) {return x._age y._age;}
};struct comp_name {bool operator()(const Person x, const Person y) {return x._name y._name;}
};
但这种方法太麻烦了而且一旦排序的标准多起来给仿函数起什么名字都是个问题所以出了lambada表达式如下
int main()
{vectorPersonv { {10,zhangsan},{40,wangwu},{20,lisi} };sort(v.begin(), v.end(), [](const Person x, const Person y) {x._age y._age; });sort(v.begin(), v.end(), [](const Person x, const Person y) {x._name y._name; });return 0;
}
可以看出lambda表达式实际上一个匿名函数跟函数很相似
2、lambda表达式语法 lambda表达式书写格式 [capture-list] (parameters) mutable - return-type { statement } lambda表达式各部分说明 [capture-list] : 捕捉列表该列表总是出现在lambda函数的开始位置编译器根据[]来判断接下来的代码是否为lambda函数捕捉列表能够捕捉上下文中的变量供lambda函数使用。(parameters)参数列表。与普通函数的参数列表一致如果不需要参数传递则可以连同()一起省略mutable默认情况下lambda函数总是一个const函数mutable可以取消其常量性。使用该修饰符时参数列表不可省略(即使参数为空)。 -returntype返回值类型。用追踪返回类型形式声明函数的返回值类型没有返回值时此部分可省略。返回值类型明确情况下也可省略由编译器对返回类型进行推导。{statement}函数体。在该函数体内除了可以使用其参数外还可以使用所有捕获到的变量。 注意 在lambda函数定义中参数列表和返回值类型都是可选部分而捕捉列表和函数体可以为空。因此C11中最简单的lambda函数为[]{}; 该lambda函数不能做任何事情 其实就是看着比较复杂跟一般的函数比起来就只是少了一个函数名多了一个捕捉列表而已所以只要把捕捉列表的功能能清楚就能很好的掌握lambda表达式 捕获列表说明 捕捉列表描述了上下文中那些数据可以被lambda使用以及使用的方式传值还是传引用。 [var]表示值传递方式捕捉变量var[]表示值传递方式捕获所有父作用域中的变量(包括this)[var]表示引用传递捕捉变量var[]表示引用传递捕捉所有父作用域中的变量(包括this)[this]表示值传递方式捕捉当前的this指针 int main()
{// 最简单的lambda表达式, 该lambda表达式没有任何意义[]{}; // 省略参数列表和返回值类型返回值类型由编译器推导为intint a 3, b 4;[]{return a 3; }; // 省略了返回值类型无返回值类型auto fun1 [](int c){b a c; }; fun1(10)couta bendl;// 各部分都很完善的lambda函数除了b是引用捕捉其他全是传值方式捕捉auto fun2 [, b](int c)-int{return b a c; }; coutfun2(10)endl;//这里提醒一下lambda表达式只能捕捉在它上面定义的变量在它之后定义的无法捕捉// 复制捕捉xint x 10;auto add_x [x](int a) mutable { x * 2; return a x; }; cout add_x(10) endl; return 0;
} 注意 a. 父作用域指包含lambda函数的语句块 b. 语法上捕捉列表可由多个捕捉项组成并以逗号分割。 比如[, a, b]以引用传递的方式捕捉变量a和b值传递方式捕捉其他所有变量 [a, this]值传递方式捕捉变量a和this引用方式捕捉其他变量 c. 捕捉列表不允许变量重复传递否则就会导致编译错误。 比如[, a]已经以值传递方式捕捉了所有变量捕捉a重复 d. 在块作用域以外的lambda函数捕捉列表必须为空。 e. 在块作用域中的lambda函数仅能捕捉父作用域中局部变量捕捉任何非此作用域或者非局部变量都会导致编译报错。 f. lambda表达式之间不能相互赋值即使看起来类型相同 void (*PF)();
int main()
{auto f1 [] {cout hello world endl; };auto f2 [] {cout hello world endl; };f1();f2();// 此处先不解释原因等lambda表达式底层实现原理看完后大家就清楚了//f1 f2; // 编译失败---提示找不到operator()// 允许使用一个lambda表达式拷贝构造一个新的副本auto f3(f2);f3();// 可以将lambda表达式赋值给相同类型的函数指针PF f2;PF();return 0;
} 规则其实并不复杂就是细节比较多主要是没咋见识过用多了就会发现它真的很香 这是上面代码的反汇编调试信息其实lambda表达式的底层实现就是仿函数只是类型名很奇怪但都是调用的operator()这个成员函数 注意一般lambda表达式的类型名都是class lambda_XXXXX这种样式的采用的是lambda_uuid的编码方式不同编译器的命名方式不同但是我们也能看出它是一个类的函数调用
对比仿函数 我们就能进一步理解捕捉列表它其实和仿函数的成员变量很相似一个是自己初始化一个是捕捉已经存在的
二、包装器---function 1.引入 截止到目前我们已经学了很3种函数---普通函数、仿函数、lambda表达式它们的类型各不相同一旦有模板需要传函数就会导致一个问题传不同类型的函数会产生多个实例如下 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;
} 而这一切都是因为类型不同所以我们需要将它们的类型进行统一包装避免这种情况发生所以出了function包装器 2.包装器介绍 std::function 在头文件 functional // 类模板 原型如下 template class T function ; // undefined template class Ret , class ... Args class function Ret ( Args ...) ; 模板参数说明 Ret: 被调用函数的返回类型 Args…被调用函数的形参 function类型的对象能用函数指针lambda表达式仿函数赋值前提是参数及返回值和function类型一样如下 int main()
{// 函数名functiondouble(double) func1 f;cout useF(func1, 11.11) endl;cout typeid(func1).name() endl;//函数对象functiondouble(double) func2 Functor();cout useF(func2, 11.11) endl;cout typeid(func2).name() endl;//lamber表达式functiondouble(double) func3 [](double d)-double { return d / 4; };cout useF(func3, 11.11) endl;cout typeid(func3).name() endl;return 0;
} 可以看出模板只实例化了一份有点类似多态传不同的函数会有不同的效果而这都是因为function包装器的类型是统一的 有人可能觉得这个用处好像不是很大但其实在大型项目中还是很有必要的而且它的应用场景远不止于此我们来看看下面的应用场景 正常来说我们得写if-else语句或者switch语句一个符号一个符号的匹配 但是现在我们可以用包装器function来简化代码使得它看起来更加优雅 3.类成员函数的包装
class Plus {
public:int ADDi(int x, int y){return x, y;}static double ADDd(double x, double y){return x y;}
private:int a;
};int main()
{
//1.注意类的成员函数的地址怎么取域名::函数名
//2.非静态成员函数第一个参数可以是类也可以是指针functionint(Plus, int, int)func1 Plus::ADDi;//可以是Plus也可以是Plus*functionint(Plus*, int, int)func2 Plus::ADDi;functiondouble(double, double)func3 Plus::ADDd;return 0;
} 三、bind std::bind函数定义在头文件中是一个 函数模板 它就像一个函数包装器(适配器)接受一个可 调用对象callable object生成一个新的可调用对象来“适应”原对象的参数列表。一般而 言我们用它可以把一个原本接收N个参数的函数fn通过绑定一些参数返回一个接收M个M 可以大于N但这么做没什么意义参数的新函数。同时使用std::bind函数还可以实现参数顺 序调整等操作。 // 原型如下
template class Fn, class... Args
/* unspecified */ bind (Fn fn, Args... args);
// with return type (2)
template class Ret, class Fn, class... Args
/* unspecified */ bind (Fn fn, Args... args); 看着概念很复杂我们来写几个看看就大致明白了
int test(int x, int y)
{return x * 2 y * 3;
}int main()
{//placeholders::_xx代表的是funcion中的参数_1代表第一个_2代表第二个以此类推functionint(int)fun1 bind(test, 2, placeholders::_1);cout fun1(1) endl;functionint(int)fun2 bind(test, placeholders::_1, 2);cout fun2(1) endl;functionint(int,int)fun3 bind(test, placeholders::_2, placeholders::_1);cout fun3(2, 3) endl;return 0;
} 这里解释一下这三个打印结果传参的对应关系如下 很显然placeholders::_1,_2,…… 对应的是function中写的参数顺序bind中第一个参数填函数名(当然仿函数对象lambda表达式都可以)后面的参数分别对应函数参数的顺序。
function包装器的参数可以少于函数的参数但是bind中传的参数一般要和原函数参数个数对应我们可以通过bind来固定一些默认的参数值或者调换一下参数的顺序让我们用起函数来更加的舒服
成员函数的bind
class Plus {
public:int ADDi(int x, int y){return x, y;}static double ADDd(double x, double y){return x y;}
private:int a 0;
};int main()
{//如果只是单纯想想使用该函数的功能可以将第一个参数写死虽然第一个参数是指针这里也可以传对象可以看作是特例functionint(int, int)func1 bind(Plus::ADDi, Plus(), placeholders::_1, placeholders::_2);cout func1(1, 2) endl;//这种是不行的右值不能被取地址//functionint(int, int)func2 bind(Plus::ADDi, Plus(), placeholders::_1, placeholders::_2);//cout func2(1, 2) endl;functiondouble(double, double)func3 bind(Plus::ADDd, placeholders::_1, placeholders::_2);cout func3(1.1, 2.3) endl;return 0;
}