C++STL中提供了bind1绑定器,通常与函数对象一起使用。
函数对象是重载了operator()函数的对象。
将二元函数对象operator()的第一个参数绑定为固定的x来构造一元函数对象。返回绑定了第一个参数的函数对象。
将二元函数对象operator()的第二个参数绑定为固定的x来构造一元函数对象。返回绑定了第二个参数的函数对象。
我们看以下代码:
#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<ctime>
using namespace std;
template<typename Container>
void showContainer(Container &con)
{
typename Container::iterator it = con.begin();
for (; it != con.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
vector<int> vec;
srand(time(nullptr));
for (int i = 0; i < 10; i++)
{
vec.push_back(rand() % 100 + 1);
}
showContainer(vec);
sort(vec.begin(), vec.end()); // 默认从小到大排序
showContainer(vec);
// greater 二元函数对象
sort(vec.begin(), vec.end(), greater<int>()); // 从大到小排序,传入函数对象
showContainer(vec);
return 0;
}
如果现在要将60按顺序插入到vector,那么就要在vector中找第一个小于60的数字。可以使用泛型算法中的find_if在指定范围中查找满足特定条件的元素。需要接收的是一个一元函数对象。
template <class InputIterator, class UnaryPredicate> InputIterator find_if (InputIterator first, InputIterator last, UnaryPredicate pred);pred:一个一元谓词,是一个可调用对象,接收一个参数,返回布尔值,返回true表示条件满足。
此时,需要从vector中取一个对象跟60比,那么不能使用库中的greater或者less函数对象,因为它们是一个二元函数对象,接收两个值。
那我们应该怎样处理呢?就引入了绑定器bind1st、bind2nd。
bind1st和bind2nd
通过绑定器绑定一个二元函数对象,就可以得到一个一元函数对象。
bind1st:要在vector中找小于60的元素,可以将greater的第一个参数绑定为60,就将greater间接的变为了一元函数对象了,传参的时候只需要传给greater的第二个参数即可。
bind2nd:要在vector中小于60的元素,可以将less的第二个参数绑定为60,就将less间接的变为了一元函数对象了,传参的时候只需要传给less的第一个参数即可。
#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<ctime>
using namespace std;
template<typename Container>
void showContainer(Container &con)
{
typename Container::iterator it = con.begin();
for (; it != con.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
vector<int> vec;
srand(time(nullptr));
for (int i = 0; i < 10; i++)
{
vec.push_back(rand() % 100 + 1);
}
showContainer(vec);
sort(vec.begin(), vec.end()); // 默认从小到大排序
showContainer(vec);
// greater 二元函数对象
sort(vec.begin(), vec.end(), greater<int>()); // 从大到小排序,传入函数对象
showContainer(vec);
// 将60按顺序插入到vector中 在vector中找第一个小于60的元素
auto it1 = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 60));
if (it1 != vec.end())
{
vec.insert(it1, 60);
}
showContainer(vec);
auto it2 = find_if(vec.begin(), vec.end(), bind2nd(less<int>(), 60));
if (it2 != vec.end())
{
vec.insert(it2, 60);
}
showContainer(vec);
return 0;
}
bind1st和bind2nd的底层实现原理
下面我们来模拟实现bind1st和bind2nd来探究以下它们的底层实现原理。
using namespace std;
template<typename Container>
void showContainer(Container &con)
{
typename Container::iterator it = con.begin();
for (; it != con.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
// 模拟实现find_if
template<typename Iterator,typename Compare>
Iterator my_find_if(Iterator first, Iterator last, Compare com)
{
for (; first != last; first++)
{
if (com(*first)) // com.operator()(*first)
return first;
}
return last;
}
// 实现_mybind1st
template<typename Compare,typename T>
class _mybind1st
{
public:
_mybind1st(Compare comp, T val)
:_comp(comp)
,_val(val)
{}
// bind1st(greater<int>(), 60)
bool operator()(const T& second)
{
return _comp(_val, second); // 底层还是二元函数对象来作用的 greater
}
private:
Compare _comp;
T _val;
};
// 模拟实现bind1st
template<typename Compare,typename T>
_mybind1st<Compare,T> mybind1st(Compare com, const T& val)
{
// 返回的是一元函数对象
return _mybind1st<Compare,T>(com, val);
}
// 实现_mybind2nd
template<typename Compare, typename T>
class _mybind2nd
{
public:
_mybind2nd(Compare comp, T val)
:_comp(comp)
, _val(val)
{}
// bind1st(greater<int>(), 60)
bool operator()(const T& first)
{
return _comp(first, _val); // 底层还是二元函数对象来作用的 greater
}
private:
Compare _comp;
T _val;
};
// 模拟实现bind2nd
template<typename Compare, typename T>
_mybind2nd<Compare, T> mybind2nd(Compare com, const T& val)
{
// 返回的是一元函数对象
return _mybind2nd<Compare, T>(com, val);
}
int main()
{
vector<int> vec;
srand(time(nullptr));
for (int i = 0; i < 10; i++)
{
vec.push_back(rand() % 100 + 1);
}
showContainer(vec);
sort(vec.begin(), vec.end()); // 默认从小到大排序
showContainer(vec);
// greater 二元函数对象
sort(vec.begin(), vec.end(), greater<int>()); // 从大到小排序,传入函数对象
showContainer(vec);
// 将60按顺序插入到vector中 在vector中找第一个小于60的元素
auto it1 = my_find_if(vec.begin(), vec.end(), mybind1st(greater<int>(), 60));
if (it1 != vec.end())
{
vec.insert(it1, 60);
}
showContainer(vec);
auto it2 = my_find_if(vec.begin(), vec.end(), mybind2nd(less<int>(), 60));
if (it2 != vec.end())
{
vec.insert(it2, 60);
}
showContainer(vec);
return 0;
}
bind1st或bind2nd绑定二元函数对象,可以生成一个一元函数对象,一元函数对象底层还是二元函数对象来实现的。
bind1st和bind2nd应用在在编程中如果想使用一个一元函数对象,但是库中没有,只有二元函数对象,我们就可以通过绑定器绑定二元函数对象的某一个参数,进而将它变成一个一元函数对象进而来使用。
bind1st和bind2nd有一个非常大的局限性,只能绑定二元函数对象,但是实际中,使用到的参数可能是三个或者以上,所以C++11就从Boost库中引入了更强大的bind绑定器。
C++11bind
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);
简单演示一下std::bind的使用方法:
#include <iostream>
#include <functional>
using namespace std;
int add(int a, int b)
{
return a + b;
}
struct Adder
{
int operator()(int a, int b)
{
return a + b;
}
};
class Calculator
{
public:
int add(int a, int b)
{
return a + b;
}
};
int main() {
// 1、绑定普通函数
// 使用 std::bind 绑定 add 函数和它的参数
auto addFunc1 = std::bind(add, 3, 5);
// 调用绑定后的可调用对象
cout << addFunc1() << endl; //就相当于调用 add(3,5)
// 使用占位符预留参数位置
auto addFunc2 = std::bind(add, std::placeholders::_1, std::placeholders::_2);
cout << addFunc2(3, 5) << endl;
// 混合使用占位符和参数
auto addFunc3 = std::bind(add, std::placeholders::_1, 5);
cout << addFunc3(3) << endl;
// 参数调换顺序
auto addFunc4 = std::bind(add, std::placeholders::_2, std::placeholders::_1);
cout << addFunc4(3, 5) << endl;
// 2、绑定函数对象
Adder adder;
auto addFunc5 = std::bind(Adder(), std::placeholders::_1, std::placeholders::_2);
cout << addFunc5(2, 2) << endl;
auto addFunc6 = std::bind(adder, std::placeholders::_1, std::placeholders::_2);
cout << addFunc6(2, 2) << endl;//相当于调用adder(1,2)
// 3、绑定lambda表达式
auto addFunc7 = std::bind([](int a, int b) {return a + b; }, std::placeholders::_1, std::placeholders::_2);
cout << addFunc7(3, 3) << endl;
// 2、绑定成员函数
Calculator calc;
auto addFunc8 = std::bind(&Calculator::add, &calc, std::placeholders::_1, std::placeholders::_2);
cout << addFunc8(4, 4) << endl;//相当于cal.add(1,2)
return 0;
}
bind绑定器,函数对象,Lambda表达式都只能在一条语句中使用,那么如果想在其他地方使用,该如何使用呢?可不可以留下函数对象的类型呢?C++11就引入了function。
C++11function
function是一个强大的包装器,可以存储、赋值任何可调用函数,包括普通函数、函数指针、成员函数指针、函数对象(仿函数)以及 lambda 表达式。它就像一个容器,能将不同类型的可调用对象统一封装起来,以统一的方式进行调用。
function本质是一个类模板,也是一个包装器。
std::function在头文件<functional>
// 类模板原型如下
template <class T> function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参
我们先来看下function的简单应用:
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<functional>
#include<ctime>
using namespace std;
void hello1()
{
cout << "hello1 world!" << endl;
}
void hello2(string str)
{
cout << str << endl;
}
int sum(int a, int b)
{
return a + b;
}
class Test
{
public:
void hello(string str)
{
cout << str << endl;
}
};
int main()
{
// 使用函数类型实例化function
// 包装普通函数
function<void()> func1 = hello1;
func1(); // func1.operator()() => hello1()
function<void(string)> func2 = hello2;
func2("hello2 world!"); // func2.operator()() => hello2()
function<int(int, int)> func3 = sum;
cout << func3(10, 20) << endl; // func3.operator()() => sum()
// 包装成员函数
Test test;
function<void(Test*, string)> func4 = &Test::hello;
func4(&test, "Test::hello world!");
// 包装函数对象
function<int(int, int)> func5 = [](int a, int b)->int {return a + b; };
cout << func5(100, 200) << endl; // operator()
return 0;
}
- 使用函数类型实例化function
- 通过function调用operator()函数的时候,需要根据函数类型传入相应参数
但是上面的例子,好像并没有看出function的好处,还不如直接调用函数呢,下面我们实现段代码看一下使用function的好处。
#include<iostream>
#include<vector>
#include<string>
#include<map>
#include<algorithm>
#include<functional>
#include<ctime>
using namespace std;
void doShowAllBooks() { cout << "查看所有书籍" << endl; }
void doBack() { cout << "还书" << endl; }
void borrow() { cout << "借书" << endl; }
void doQueryBooks() { cout << "查询书籍信息" << endl; }
void dologinOut() { cout << "注销" << endl; }
int main()
{
int choice;
map<int, function<void()>> actionMap;
actionMap.insert({ 1, doShowAllBooks });
actionMap.insert({ 2, doBack });
actionMap.insert({ 3, borrow });
actionMap.insert({ 4, doQueryBooks });
actionMap.insert({ 5, dologinOut });
for (;;)
{
cout << "-----------------" << endl;
cout << "查看所有书籍" << endl;
cout << "还书" << endl;
cout << "借书" << endl;
cout << "查询书籍" << endl;
cout << "注销" << endl;
cout << "-----------------" << endl;
cin >> choice;
auto it = actionMap.find(choice);
if (it == actionMap.end())
{
cout << "输入数字无效,请重新选择!" << endl;
}
else
{
it->second();
}
}
// 不实用,这段代码无法闭合,无法做到“开-闭”原则
/*switch (choice)
{
case 1:
cout << "查看书籍" << endl;
break;
case 2:
cout << "还书" << endl;
break;
case 3:
cout << "借书" << endl;
break;
case 4:
cout << "查询" << endl;
break;
case 5:
cout << "注销" << endl;
break;
default:
break;
}*/
return 0;
}
function函数对象类型的实现原理
#include<iostream>
#include<string>
#include<functional>
using namespace std;
void hello(string str)
{
cout << str << endl;
}
int sum(int a, int b)
{
return a + b;
}
// 通用模板,作为偏特化的基础
template<typename Fty>
class myfunction {};
/*
// 模板偏特化允许针对特定的模板参数类型或者参数组合,提供专门的实现
template<typename R, typename A1>
class myfunction<R(A1)>
{
public:
using PFUNC = R(*)(A1);
myfunction(PFUNC pfunc)
:_pfunc(pfunc)
{}
R operator()(A1 arg)
{
return _pfunc(arg); // hello("hello world!")
}
private:
PFUNC _pfunc;
};
template<typename R, typename A1, typename A2>
class myfunction<R(A1, A2)>
{
public:
using PFUNC = R(*)(A1, A2);
myfunction(PFUNC pfunc)
:_pfunc(pfunc)
{}
R operator()(A1 arg1, A2 arg2)
{
return _pfunc(arg1, arg2); // hello("hello world!")
}
private:
PFUNC _pfunc;
};
*/
// 函数参数不确定,难道要特化很多模板出来吗?不需要,C++11提供了可变参数模板
template<typename R, typename... A>
class myfunction<R(A...)>
{
public:
using PFUNC = R(*)(A...);
myfunction(PFUNC pfunc)
:_pfunc(pfunc)
{}
R operator()(A... arg)
{
return _pfunc(arg...); // hello("hello world!") sum(10,20)
}
private:
PFUNC _pfunc;
};
int main()
{
myfunction<void(string)> func1(hello);
func1("hello world!"); // func1.operator()("hello world!")
myfunction<int(int, int)> func2(sum);
cout << func2(10, 20) << endl; //func2.operator()(10,20)
return 0;
}
function其实就是一个函数对象(重载了operator()函数),底层封装了一个函数指针,指向了要包装的函数,通过可变参数模板,operator()()可以接收类型及任意数量的参数,将参数传递给内部,内部使用函数指针进行函数的调用。
bind与function实现线程池
#include<iostream>
#include<thread>
#include<vector>
#include<functional>
class Thread
{
public:
Thread(std::function<void()> func)
: _func(func)
{
}
~Thread() {}
std::thread start()
{
std::thread t(_func);
return t;
}
private:
std::function<void()> _func;
};
class ThreadPool
{
public:
void startPool(int size)
{
for (int i = 0; i < size; i++)
{
// 使用bind绑定器绑定线程函数
_pool.push_back(std::make_unique<Thread>(std::bind(&ThreadPool::runInThread, this, i)));
// 使用Lambda代替bind
// auto Func = [this, i](){ runInThread(i); };
// _pool.push_back(std::make_unique<Thread>(Func));
}
for (int i = 0; i < size; i++)
{
_handler.push_back(_pool[i]->start());
}
for (std::thread& t : _handler)
{
t.join();
}
}
private:
std::vector<std::unique_ptr<Thread>> _pool; // 线程池
std::vector<std::thread> _handler; // 线程列表
// 线程执行的方法
void runInThread(int id)
{
std::cout << "call runInThread! id:" << id << std::endl;
}
};
int main()
{
ThreadPool pool;
pool.startPool(10);
return 0;
}
总结:
std::function和std::bind虽然都与可调用对象相关,但它们的功能和用途各有侧重。std::function主要用于存储和调用可调用对象,实现回调和多态;std::bind主要用于参数绑定和调整参数顺序,简化函数调用。