提高网站seo,建筑设计网站issuu,茂名网络推广,上海网络维护培训班目录
lambda 表达式
可变模板参数
C11新类的默认函数
包装器
function
bind lambda 表达式 lambda 表达式也是可调用对象#xff0c;在C语言中就有函数指针#xff0c;但是函数指针比较复杂。 而在C11之前#xff0c;也有仿函数#xff0c;使用仿函数#xff0c;还…目录
lambda 表达式
可变模板参数
C11新类的默认函数
包装器
function
bind lambda 表达式 lambda 表达式也是可调用对象在C语言中就有函数指针但是函数指针比较复杂。 而在C11之前也有仿函数使用仿函数还需要再定义一个类专门实现仿函数所以还是比较麻烦。 而 lambda 还是比较简单的。
下面看这一段代码
class fruit
{
public:fruit(string name, int price, int evaluate):_name(name),_price(price),_evaluate(evaluate){}
bool operator(fruit f){return _price f._price;}
string _name;int _price;int _evaluate;
};
void test1()
{auto lamd [](int x, int y)-int {return x y; };vectorfruit fruits{ {香蕉, 10,9}, {苹果, 13, 8}, {葡萄, 16, 7}, {梨,17,4}, {榴莲, 50, 8}};sort(fruits.begin(), fruits.end());sort(fruits.begin(), fruits.end());sort(fruits.begin(), fruits.end());
}
这里如果排序的话那么时不可以的如果想排序除非重载比较函数但是比较函数也只能重载一个。
其实还可以写仿函数而如果我们像使用多个成员变量的比较也是比较方便的下面看一下仿函数的处理方法。
class nameLess
{
public:bool operator()(fruit f1, fruit f2){return f1._name f2._name;}
};
class priceLess
{
public:bool operator()(fruit f1, fruit f2){return f1._price f2._price;}
};
class evaluateLess
{
public:bool operator()(fruit f1, fruit f2){return f1._evaluate f2._evaluate;}
};
void test1()
{auto lamd [](int x, int y)-int {return x y; };vectorfruit fruits{ {香蕉, 10,9}, {苹果, 13, 8}, {葡萄, 160, 7}, {梨,17,4}, {榴莲, 50, 8}};sort(fruits.begin(), fruits.end(), nameLess());sort(fruits.begin(), fruits.end(), priceLess());sort(fruits.begin(), fruits.end(), evaluateLess());
}
下面只需要把该类的对象传过去就可以调用仿函数了其实还可以使用 lambda 表达式首先看一下 lambda 表达式如果写 lambda 表达式没有类型所以可以直接写 lambda 的对象 而如果要定义的话那么可以把类型定义为 auto lambda 表达式里面的内容有四个主体 捕捉列表 参数列表 返回值 函数体 auto lamd [捕捉列表](参数列表)-返回值{函数体;}; 上面就是 lambda 表达式的定义而该对象也可以直接用作可调用对象 void test1()
{auto lamd [](int x, int y)-int {return x y; };vectorfruit fruits{ {香蕉, 10,9}, {苹果, 13, 8}, {葡萄, 160, 7}, {梨,17,4}, {榴莲, 50, 8}};
auto name_less [](fruit x, fruit y)-bool {return x._name y._name; };auto price_less [](fruit x, fruit y)-bool {return x._price y._price; };auto evaluate_less [](fruit x, fruit y)-bool {return x._evaluate y._evaluate; };
sort(fruits.begin(), fruits.end(), name_less);sort(fruits.begin(), fruits.end(), price_less);sort(fruits.begin(), fruits.end(), evaluate_less);
}
上面就是调用使用 lambda 表达式来控制 sort 排序 lambda 比仿函数还是要容易一点
下面就仔细介绍一下 lambda 表达式
首先是参数列表 参数列表就是和正常函数一样传参数的所以这个参数列表和函数是一样的。 如果没有参数的话也是可以省略的。
返回值 返回值的声明是箭头- 后加 返回值类型 但是返回值是可以不写的因为可以自动推导所以返回值是可以省略的 auto lamd [](){};
捕捉列表 捕捉列表里面写的就是想要捕捉的值如果不想再参数列表里面写的话就可以写再捕捉列表里面。 捕捉列表里面可以写的值 value: 也就是局部的变量都可以写 value表示以引用的方式捕捉该变量 还有符号表示将所有的局部变量都捕捉不过捕捉的局部变量是不能修改的 表示将所有的局部变量都以引用的方式捕捉 还可以混着写
函数体 函数体和函数是一样的所以这里也不多介绍了 以值方式捕捉
int a 10;
auto lamd [a] {
};
以值方式捕捉的话是不能被修改的
int a 10;
auto lamd [a] {a 20;
};
上面这样写是会报错的那么如果想要修改怎么办呢
可以加 mutable 让值捕捉的可以被修改但是值捕捉指数拷贝所以修改也并不会影响原来的值
int a 10;
auto lamd1 [a]() mutable {a 20;
};
但是如果要加 mutable 的时候就不能省略参数列表了。
以引用捕捉的 auto lamd2 [a] {a 20;};
如果是以引用捕捉的话是可以修改的
值捕捉所有的变量 int b 20;auto lamd2 [] {cout a endl;cout b endl;};
同样是以值捕捉的话是不能修改的而上面定义的变量 a 和 b 都可以被访问
引用捕捉所有的变量 auto lamd2 [] {cout a endl;cout b endl;};
以引用捕捉的话也可以访问所有的变量同时也可以修改
混合使用 auto lamd2 [, b] {cout a endl;cout b endl;};
这种混合使用也是可以的但是不能是重复的也就是继续捕捉 b,如果是 的话那么也就不能以值捕捉其他变量同时也不能即 又 。
可变模板参数 可变模板参数和可变参数列表很相似 而之前再C语言中见过的可变参数列表是 printf 和 scanf
下面看一下可变模板参数的使用
templateclass ...Args
void func(Args ...args)
{
} 上面就是声明一个可变模板参数类型的函数 其中上面的 Args 就是函数包再写的时候必须有 ... 三个点而这三个点也表示模板参数的展开 Args 就是模板传过来的类型也就是类似于 K/T/V 这种一样只是一个名字所有不一定叫 Args
templateclass T
void func(const T val)
{cout val endl;
}
templateclass T, class ...Args
void func(const T val, Args ...args)
{cout val endl;func(args...);
}
void test2()
{func(1, 2.5, string(hello world));
}
这里写一个可以打印可变参数列表的一个函数 首先是 func 函数里面传入了三个值一个值传到了 T, 另外的值传到了参数包里面 func 函数打印第一个值然后将参数包中的值递归式的调用自己没传给自己这样就可以实现打印 但是这里需要一个截至函数也就是只剩下一个的时候那么就是递归结束的条件
可变模板参数的应用不过如此还有其他的用处。
看下面这一段代码
class Date
{
public:Date(int year, int month, int day):_year(year),_month(month),_day(day){}
void print(){cout _year - _month - _day endl;}
private:int _year 1;int _month 1;int _day 1;
};
templateclass ...Args
Date* Create(Args ...args)
{return new Date(args...);
}
void test3()
{Date* ret Create(2001, 10, 10);ret-print();
}
上面 test 函数调用 Create 函数 Cretare 函数是可变模板参数所以可以传任意类型个数的值这里传入三个整型然后让 Create 函数调用 Date的构造函数然后构造一个 date 对象。
实际上还可以直接传入 date 对象
void test3()
{Date* d1 Create(2001, 10, 10);d1-print();
Date* d2 Create(*d1);d2-print();
}
这里将 d1 解引用后传入到 Create 函数中由于是可变模板参数所以可以接受任意类型个数的参数所以这里将 d1 传入后调用Date 的拷贝构造函数然后拷贝构造一个 d2
结果
2001-10-10
2001-10-10
介绍完这个之后可以看一下C11 里面 stl 容器里面的 emplace 接口
template class... Argsvoid emplace_back (Args... args);
那么它就是利用了模板的可变参数如果 args 是一个插入的对象的参数那么就该函数就调用 new 对应的节点构造函数或者是拷贝构造如果直接是对象的话那么就是拷贝构造但是如果写了移动构造就有可能是移动构造如果是参数的话那么就是构造函数
看下面代码
void test4()
{listpairint, int lp;lp.emplace_back(10, 10);lp.emplace_back(make_pair(20, 20));
}
上面代码里面第一次调用emplace_back 是传的是参数所以会调用构造函数而第二个会走移动构造。
C11新类的默认函数 有了上面的右值引用实际上到C11里面多了两个默认函数。 移动构造 移动赋值
但是既然时默认函数那么其默认时如何生成的默认生成的又会做什么 其实移动构造和移动赋值的默认生成行为一样 对于移动构造来说如果没有显示的写移动构造和移动赋值并且还没有写拷贝构造、复制重载、析构函数那么就会默认生成一个默认生成的移动构造会对内置成员变量进行浅拷贝对于自定义类型会调用其移动构造如果自定义类型没有移动构造那么就会调用其拷贝构造。 对于移动赋值来说如果没有显示的写移动构造和移动赋值并且还没有写拷贝构造、复制重载、析构函数那么就会默认生成一个默认生成的移动赋值会对内置成员变量进行浅拷贝对于自定义类型会调用其移动赋值如果自定义类型没有移动赋值那么就会调用其复值重载。
上面就是C11新类的默认函数。
再C11之前默认函数就有6个但是我们常用的只有4个再加上C11的两个那么就是6个默认函数。 默认构造函数 默认拷贝构造 默认复制重载 默认析构函数 默认移动构造 默认移动赋值
包装器
function
这里先看这个函数
templateclass F, class T
T Fun(F f, T val)
{static int count 0;
cout (count) endl;cout (count) endl;
return f(val);
} 该函数里面有一个 static 的 count 对象如果时相同类型调用该函数的话那么都使用的是一个 count. 但是如果是不同的类型的话那么这里的 count 和 count 的地址都是不同的。 下面我们用三个可调用对象传给该函数这三个对象的参数返回值都一样只是里面的实现不同我们能不能调用到同一个函数呢
// 函数
double Func1(double val)
{return val / 10;
}
// 仿函数
class Func2
{
public:double operator()(double val){return val / 20;}
};
void test1()
{// lambda 表达式auto Func3 [](double val) {return val / 30; };
cout Fun(Func1, 15) endl endl;cout Fun(Func2(), 15) endl endl;cout Fun(Func3, 15) endl endl;
} 这里我们分别由三个可调用对象来调用该函数 函数指针 仿函数 lambda 表达式
结果
1
00A7A140
1
1
00A7A144
0
1
00A7A148
0 这里看到这里的地址和值基本都是不一样的。 说明这三个函数都调用到了不同的函数。 那么这三个函数的参数返回值都相同为什么调用不到同一个函数呢 因为他们的类型不相同。 那么我们想要让他们调用到同一个函数怎么办 下面就看一下包装器我们使用包装器使他们的的类型都相同。
先看一下他的类型
template class Ret, class... Args
class functionRet(Args...); 这里的包装器使一个模板。 他的参数好像模板的特化而 Ret 表示的就是函数的返回值而后面的参数包里面写的就是函数的参数。 由于不知道函数由多少个参数所以就使用可变模板参数。
下面我们将那三个函数都包装成相同的类型
void test2()
{auto Func3 [](double val) {return val / 30; };
// 包装functiondouble(double) fun1 Func1;functiondouble(double) fun2 Func2();functiondouble(double) fun3 Func3;// 返回值 参数//现在这三个函数的类型相同现在调用 Fun函数
cout Fun(fun1, 15) endl endl;cout Fun(fun2, 15) endl endl;cout Fun(fun3, 15) endl endl;
}
结果
1
009FE4FC
1
2
009FE4FC
0
3
009FE4FC
0 现在看到这里的 count 的地址也一样了并且我们对 count 加了三次。
bind
除了上面这样包装函数那么我们还可以怎么做
我们还可以将参数与位置绑定
void test3()
{auto sub [](double a, double b) {return a - b; };
functiondouble(double, double) sub1 bind(sub, placeholders::_1, placeholders::_2);cout sub1(10, 5) endl;
} 这里我们使用 bind 函数bind 函数里面的第一个参数是想要绑定的函数。 后面的参数是每个调用绑定函数的时候按顺序传给绑定的函数。 而这个 placeholder::x 这个表示给 sub1 传参时候的第几个参数第一个参数就会 placeholders::1 的位置同理第二个参数就会传给第二个。
下面看一下结果其实就明白了。
结果
5
下面我们再换一下参数的位置
void test3()
{auto sub [](double a, double b) {return a - b; };
functiondouble(double, double) rsub bind(sub, placeholders::_2, placeholders::_1);cout rsub(10, 5) endl;
}
结果
-5
其实 bind 不光可以绑定参数的位置还可以绑定固定的参数
void test4()
{auto Fun [](int a, int b, double rate) {return (a b) * rate;};
functiondouble(int, int) fun1 bind(Fun, placeholders::_1, placeholders::_2, 1.1);cout fun1(10,20) endl;
} 这里将 1.1 固定的绑定再 rate 上。
结果
33
那么这个有什么用呢 这个可以将特定参数与位置绑定那么有一些函数经常需要传固定参数的函数那么就可以使用 bind 减少我们传参。 还可以实现打折如果是 vip 的话那么计算价钱的方式打折就更多一点如果是普通用户那么就是打折的话少一点。 其实还可以有很多用处。