中国建信网官方网站,做类似淘宝网站怎么做,百度搜索关键词,成都网站建设学习c11之前#xff0c;STL中提供了bind1st以及bind2nd绑定器 首先来看一下他们如何使用#xff1a; 如果我们要对vector中的元素排序#xff0c;首先会想到sort#xff0c;比如#xff1a;
void output(const vectorint vec)
{for (auto v : vec) {cout 11之前STL中提供了bind1st以及bind2nd绑定器 首先来看一下他们如何使用 如果我们要对vector中的元素排序首先会想到sort比如
void output(const vectorint vec)
{for (auto v : vec) {cout v ;}cout endl;
}int main() {vectorint vec;srand(time(nullptr));for (int i 0; i 20; i) {vec.push_back(rand() % 100 1);}output(vec);sort(vec.begin(), vec.end());output(vec);//greater 从大到小排序sort(vec.begin(), vec.end(), greaterint());output(vec);//less 从小到大排序sort(vec.begin(), vec.end(), lessint());output(vec);return;
}sort最后一个参数传入的greater或less都被称为函数对象顾名思义表现像函数的对象因为他们的调用都是在后面加上()。 其实这是因为他们都重载了operator()。 来看下greater的定义
templateclass _Ty voidstruct greater{ // functor for operator_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty first_argument_type;_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty second_argument_type;_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool result_type;constexpr bool operator()(const _Ty _Left, const _Ty _Right) const{ // apply operator to operandsreturn (_Left _Right);}};可以看到确实重载了operator()需要传入两个参数所以它还是一个二元函数对象。函数对象的概念至关重要 那什么时候会用到bind1st、bind2nd呢 比如我们现在要找到第一个小于70的位置插入70。 可以使用find_if auto it1 find_if(vec.begin(), vec.end(),bind1st(greaterint(), 70));if (it1 ! vec.end()) {vec.insert(it1, 70);}这里使用bind1st(greaterint(), 70)作为find_if的第三个参数bind1st的作用首先将70绑定到二元函数对象greater的第一个参数上其次将二元函数对象greater转为一元函数对象因为70已知了传入到find_if的第三个参数中。这便是他的应用场景或者还可以使用bind2nd和less的搭配 auto it1 find_if(vec.begin(), vec.end(),bind2nd(lessint(), 70));if (it1 ! vec.end()) {vec.insert(it1, 70);}关于bind1st(greater(), 70)和bind2nd(less(), 70)的理解 因为我们要找小于70的位置所以 对于greater来说left right所以绑定到第一个位置 对于less来说left right所以绑定到第二个位置 理解了绑定器后再来看看function function需要一个函数类型进行实例化
void hello1()
{cout hello world! endl;
}void hello2(string str)
{cout str endl;
}class Test
{
public:void hello(string str) { cout str endl; }
};int main() {functionvoid() func1 hello1;//functionvoid() func1(hello1);func1();//func1.operator() hello1();functionvoid(string) func2 hello2;func2(gao);//func2.operator()(string str) hello2(str);functionint(int, int) func3 [](int a, int b) - int { return a b; };cout func3(100, 200) endl;//通过function调用类的成员方法functionvoid(Test*, string) func5 Test::hello;func5(Test(), call Test::hello!);return 0;
}对function的调用实际上是调用了function的()重载从而调用原函数。上面的例子中可以看到lambda表达式也可以通过function调用。这其实就说明了function的真正用途保存函数对象的类型也是对函数对象的封装。这也是它和c语言的函数指针的区别lambda无法通过函数指针调用。
现在有这样一个场景 两多个函数有大部分的代码都是一样的其中只有一两行代码有不一样的地方我们可以对这个不一样的地方使用function做一个抽象比如 有两个vector打印函数一个打印模50的元素一个打印大于10的元素
void print(vectorint number, functionbool(int) filter) {for (const int i : number) {if (filter(i)) {cout i endl;}}
}print(numbers, [](int i){ return i % 5 0; });
print(numbers, [](int i){ return i 10; });这样就不用定义两个不同的打印函数了。
关于闭包的概念 下面是维基百度对于闭包的定义 在计算机科学中闭包英语Closure又称词法闭包Lexical Closure或函数闭包function closures是引用了自由变量的函数。 这个被引用的自由变量将和这个函数一同存在即使已经离开了创造它的环境也不例外。
简单来说闭包可以记忆住创建它时候的那些变量。 下面我们再通过一个例子来说明。 现在假设我们的需求是获取一个集合中最小和最大值并在稍后的时候可能是另外一个函数中打印它们。 这里我们常规的做法通常是通过一个函数获取集合的最大最小值然后保存住最后在需要的时候访问这两个值然后打印它们。 这样做就会需要解决如何保存和传递最大最小这两个值。 但实际上这里我们可以考虑用闭包来实现这个功能让闭包把最大最小两个值捕获下来然后在需要的地方调用就可以了。
请看一下下面这段代码
void getMinMax(vectorint number, functionvoid () printer) {int min number.front();int max number.front();for (int i : number) {if (i min) {min i;}if (i max) {max i;}}printer [] () {cout min: min endl;cout max: max endl;};
}这里我们通过functionvoid () printer传递出这个闭包。 然后在需要的地方这样即可
functionvoid() printer;
getMinMax(numbers, printer);
......printer();这里的printer其实是我们前面从getMinMax函数出传出的闭包这个闭包捕获了min和max。我们直接传递这个闭包给需要的地方使用而不用传递裸的两个数值是不是优雅的不少
bind
/*
c11 bind绑定器 - 返回的结果还是一个函数对象
*/void hello(string str) { cout str endl; }
int sum(int a, int b) { return a b; }class Test
{
public:int sum(int a, int b) { return a b; }
};int main()
{bind(hello, hello, bind!)();cout bind(sum, 10, 20)() endl;cout bind(Test::sum, Test(), 20, 30)() endl;//参数占位符 绑定器出了语句无法继续使用bind(hello, placeholders::_1)(hello bind 2!);cout bind(sum, placeholders::_1, placeholders::_2)(200, 300) endl;//此处把bind返回的绑定器binder就复用起来了functionvoid(string) func1 bind(hello, placeholders::_1);func1(hello gao);return 0;
}使用bind和function的线程池例子
class Thread
{
public://接收一个函数对象参数都绑定了所以不需要参数Thread(functionvoid() func) : _func(func) {}thread start(){thread t(_func);return t;}
private:functionvoid() _func;
};class ThreadPool
{
public:ThreadPool() {}~ThreadPool() {for (int i 0; i _pool.size(); i) {delete _pool[i];}}void startPool(int size){for (int i 0; i size; i) {//成员方法充当线程函数绑定this指针_pool.push_back(new Thread(bind(ThreadPool::runInThread, this, i)));}for (int i 0; i size; i) {_handler.push_back(_pool[i]-start());}for (thread t : _handler) {t.join();}}
private:vectorThread* _pool;vectorthread _handler;void runInThread(int id) {cout call runInThread! id: id endl;}
};int main()
{ThreadPool pool;pool.startPool(10);return 0;
}lambda匿名函数对象 lambda表达式的语法
[捕获外部变量](形参列表)-返回值{操作代码}[]:表示不捕获任何外部变量
[]:表示以传值的方式捕获外部的所有变量
[]:表示以传引用的方式捕获外部的所有变量
[this]:捕获外部的this指针
[,a]: 表示以传值的方式捕获外部的所有变量但是a变量以传引用的方式捕获
[a,b]:表示以值传递的方式捕获外部变量a和b
[a,b]:a以值传递捕获b以引用捕获使用举例
int main()
{auto func1 []()-void {cout hello world! endl; };func1();//[]:表示不捕获任何外部变量//编译报错/*int a 10;int b 20;auto func3 [](){int tmp a;a b;b tmp;};*///以值传递ab,lambda实现的重载函数operator()中是const方法不能修改成员变量//如果一定要修改将lambda修饰成mutable但是这并不会改变a的值因为这是值传递//int a 10;//int b 20;//auto func3 [a, b]() /*mutable*///{// int tmp a;// a b;// b tmp;//};vectorint vec;vec.push_back(1);vec.push_back(2);vec.push_back(3);for_each(vec.begin(), vec.end(), [](int a) {cout a endl;});return 0;
}class Data
{
public:Data(int a, int b) : ma(a), mb(b) {}int ma;int mb;
};int main()
{mapint, functionint(int, int) caculateMap;caculateMap[1] [](int a, int b)-int {return a b; };caculateMap[2] [](int a, int b)-int {return a - b; };cout caculateMap[1](1, 2) endl;//智能指针自定义删除器unique_ptrFILE, functionvoid(FILE*)ptr1(fopen(data.txt, w), [](FILE *pf) { fclose(pf); });//优先队列using FUNC functionbool(Data, Data);priority_queueData, vectorData, FUNCmaxHeap([](Data d1, Data d2)-bool{return d1.mb d2.mb;});maxHeap.push(Data(10, 20));return 0;
}lambda表达式是如何实现的 其实是编译器为我们了创建了一个类这个类重载了()让我们可以像调用函数一样使用。所以你写的lambda表达式和真正的实现是这个样子的 而对于捕获变量的lambda表达式来说编译器在创建类的时候通过成员函数的形式保存了需要捕获的变量所以看起来是这个样子 似乎也没有什么神奇的地方。但正是由于编译器帮我们实现了细节使我们的代码变得优雅和简洁了许多。
参考文章https://paul.pub/cpp-lambda-function-bind/