系列文章目录
前言
C++ 这门编程语言的历史可以追溯至 1979 年,当时的 Bjarne Stroustrup(C++ 之父,后续简称 Stroustrup)还在使用 Simula 语言进行开发工作。
1998 年,C++ 标准委员会发布了第一版 C++ 标准,并将其命名为 C++ 98 标准。据不知名人士透露,《带注释的C++参考手册》这本书对 C++ 98 标准的制定产生了很大的影响。
经过作者的不断迭代,一本书往往会先后发布很多个版本,其中每个新版本都是对前一个版本的修正和更新。C++ 编程语言的发展也是如此。截止到目前(2020 年),C++ 的发展历经了以下 3 个个标准:
2011 年,新的 C++ 11 标准诞生,用于取代 C++ 98 标准。
2014 年,C++ 14 标准发布,该标准库对 C++ 11 标准库做了更优的修改和更新;
2017 年底,C++ 17 标准正式颁布;
虽然学习 C++11 需要花些时间,但这是非常值得的;C++11 非常实用,它不但提高了开发效率,还让程序更加健壮和优雅。程序员应该乐于升级换代已有的知识,而学习和使用 C++11 早就是大势所趋。
|版本声明:山河君,未经博主允许,禁止转载
一、std::bind概述
1.std::bind简介
说起bind函数,应该有不少人第一反应是TCP/IP编程中用于将套接字和本地地址进行绑定的用法。而本文说的bind同样是用于绑定。但是是用于将已有变量绑定到可调用函数的参数上。
早在C98中,就有两个函数bind1st和bind2nd,他们被用于绑定已有变量到可执行函数的第一个参数和第二个参数,相同的是他们都只能绑定一个参数。
如果看到这还是不太明白,我们直接看例子,假如现在想遍历一个容器中小于5的值
void fun(int i = 0, int j = 0)
{
if (i < j)
cout << i << endl;
}
int main(int argv, char* argc[])
{
vector<int> vecArray = { 1,2,3,4,5,6,7,8,9,10 };
for_each(vecArray.begin(), vecArray.end(), bind2st(fun, 5));
return 0;
}
此时5就被绑定到fun函数的第二个参数上了。但是bind1st和bind2st局限性太大,所以C++11中推出了std::bind。
2.std::bind原型
直接跟进bind函数,发现它实际为两个模板函数
// FUNCTION TEMPLATE bind (implicit return type)
template <class _Fx, class... _Types>
_NODISCARD _CONSTEXPR20 _Binder<_Unforced, _Fx, _Types...> bind(_Fx&& _Func, _Types&&... _Args) {
return _Binder<_Unforced, _Fx, _Types...>(_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...);
}
// FUNCTION TEMPLATE bind (explicit return type)
template <class _Ret, class _Fx, class... _Types>
_NODISCARD _CONSTEXPR20 _Binder<_Ret, _Fx, _Types...> bind(_Fx&& _Func, _Types&&... _Args) {
return _Binder<_Ret, _Fx, _Types...>(_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...);
}
它实际上是使用一个可调用对象,通过绑定一些参数生成一个新的可调用对象。这里注意一下细节,说的是可调用对象而不是可调用接口函数。
std::bind绑定的可调用对象:
可调用对象 |
---|
普通函数、静态函数、模板函数 |
类成员函数、类成员静态函数、类成员模板函数、类成员变量、类内部绑定 |
仿函数、Lambda表达式 |
二、使用std::bind绑定
1.绑定第一类
普通函数、静态函数、模板函数
输入两个数,打印两个数中较大的一个
void fun1(int i, int j){ cout << (i > j ? i : j) << endl; } //普通函数
static void fun2(int i, int j) { cout << (i > j ? i : j) << endl; } //静态函数
template <typename T1 = int, typename T2 = int>
void fun3(T1 i, T2 j) { cout << (i > j ? i : j) << endl; } //模板函数
int main()
{
auto fun1Test = bind(fun1, 1, 2);
auto fun2Test = bind(fun2, 1, 2);
auto fun3Test = bind(fun3<>, 1, 2);
fun1Test();
fun2Test();
fun3Test();
return 0;
}
毫无疑问结果都为2.
2.绑定第二类
类成员函数、类成员静态函数、类成员模板函数、类成员变量、类内部绑定
这部分比较重要的就是要明白普通函数和类成员函数有什么区别,我们都知道的是在函数指针上面,类成员函数指针不仅要指定目标函数的形参列表和返回类型,还必须指出成员函数所属的类。
当然类的静态成员不属于任何对象,因此无须特殊的指向静态成员的指针,指向静态成员的指针与普通指针没有什么区别。
所以我们在bind类成员函数时,需要显示的传入类对象、或者类对象的引用、或者类对象的指针
如下:
class Test
{
public:
void fun1(int i, int j) { cout << (i > j ? i : j) << endl; } //类成员普通函数
static void fun2(int i, int j) { cout << (i > j ? i : j) << endl; } //类静态函数
template <typename T1 = int, typename T2 = int>
void fun3(T1 i, T2 j) { cout << (i > j ? i : j) << endl; } //类模板函数
void fun4() { auto fun1Test = bind(&Test::fun1, this, 1, 2); fun1Test(); }; //类内部使用
public:
int m_X = 10;
};
int main()
{
Test test;
Test* pTest = &test;
auto fun1Test = bind(&Test::fun1, test, 1, 2); //传入类对象
auto fun2Test = bind(Test::fun2, 1, 2); //因为静态函数,所以无需传入类
auto fun3Test = bind(&Test::fun3<>, &test, 1, 2); //传入类引用
auto memberVar = std::bind(&Test::m_X, pTest); //传入类对象指针
fun1Test();
fun2Test();
fun3Test();
test.fun4(); //类内部使用,直接使用this
cout << memberVar() << endl;
return 0;
}
3.绑定第三类
仿函数、Lambda表达式
class Compare {
public:
void operator()(int i, int j) { cout << (i > j ? i : j) << endl; }
};
int main()
{
auto fun1 = bind(Compare(), 1, 2); //仿函数自动实例化
auto fun2 = bind([](int i, int j) {cout << (i > j ? i : j) << endl; }, 1, 2); //匿名函数
fun1();
fun2();
return 0;
}
三、占位符
占位符是用于表明要绑定的参数在生成新的可执行对象的位置。有的时候我们不需要立马进行绑定,或者不需要全部进行绑定,甚至是需要改变参数传入的顺序时候使用。
void fun(int i, int j = 2) { cout << (i > j ? i : j) << endl; }
int main()
{
auto fun1 = bind(fun, 1, std::placeholders::_1);
auto fun2 = bind(fun, std::placeholders::_1, 2); //第二个参数必须输入,这个时候不识别默认参数
auto fun3 = bind(fun, std::placeholders::_2, std::placeholders::_1);
fun1(2);
fun2(1);
fun3(1, 2); //此时1作为第二个参数输入,2作为第一个参数输入
return 0;
}
总结
仍在持续更新中~
如果对您有所帮助,请点个赞!