目录
1、引言
2、std::function函数模板类
3、std::bind标准库函数
4、std::bind和std::function配合使用
VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章正在更新中...)https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html C++11新特性很重要,作为C++开发人员很有必要去学习,不仅笔试面试时会涉及到,开源代码中也在大规模的使用。以很多视频会议及直播软件都在使用的开源WebRTC项目为例,WebRTC代码中大篇幅地使用了C++11及以上的新特性,要读懂其源码,必须要了解这些C++的新特性。所以,接下来一段时间我将结合工作实践,给大家详细讲解一下C++11的新特性,以供借鉴或参考。
1、引言
C++11引入了一个模板类std::function以及一个标准库函数std::bind,这两个特性使得C++变得更加灵活。使用std::function类模板可以实现对调用对象的包装。调用标准库函数std::bind在原有函数的基础上生成一个新的函数,方便调用。下面就来详细讲解这两个新特性。
2、std::function函数模板类
在C++中,可调用实体主要包括:函数、函数指针、函数引用、可以隐式转换为函数指定的对象,或者实现了opetator()的对象。
C++11中,新增加了一个std::function类模板,它是对C++中现有的可调用实体的一种类型安全的包裹。std::function可以存储,复制和调用任何可调用目标的实例,例如函数,lambda表达式,绑定表达式或其他函数对象,以及指向成员函数和指向数据成员的指针。通过指定它的模板参数,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟执行它们。
该模板类中所存储的可调用对象被称为目标的std::function。如果一个std::function实例不包含目标,则将其称为空。调用空的std::function对象会导致抛出异常std::bad_function_call。
#include <iostream>
#include <functional> //std::cout
using namespace std;void func(void)
{//普通全局函数cout << __func__ << endl;
}class Foo
{
public:static int foo_func(int a){//类中静态函数cout << __func__ << "(" << a << ") ->: ";return a;}
};class Bar
{
public:int operator()(int a){//仿函数cout << __func__ << "(" << a << ") ->: ";return a;}
};int main()
{//绑定一个普通函数function< void(void) > f1 = func;f1();//绑定类中的静态函数function< int(int) > f2 = Foo::foo_func;cout << f2(111) << endl;//绑定一个仿函数Bar obj;f2 = obj;cout << f2(222) << endl;/*运行结果:funcfoo_func(111) ->: 111operator()(222) ->: 222*/return 0;
}
std::function对象最大的用处就是在实现函数回调,使用者需要注意,它不能被用来检查相等或者不相等,但是可以与NULL或者nullptr进行比较。
3、std::bind函数
std::bind是这样一种机制,它可以预先把指定可调用实体的某些参数绑定到已有的变量,产生一个新的可调用实体,这种机制在回调函数的使用过程中也颇为有用。
C++98中,有两个函数bind1st和bind2nd,它们分别可以用来绑定functor的第一个和第二个参数,它们都是只可以绑定一个参数,各种限制,使得bind1st和bind2nd的可用性大大降低。
在C++11中,提供了std::bind,它绑定的参数的个数不受限制,绑定的具体哪些参数也不受限制,由用户指定,这个bind才是真正意义上的绑定。可以将bind函数看成一个通用的函数适配器,它接收一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。调用bind的一般形式:
auto newCallable = bind( callable, arg_lsit);
其中,newCallable是个可调用对象,arg_lsit是一个逗号分隔的参数列表,对应给callable可调用对象传递的参数。当我们调用新的newCallable时,newCallable会调用callable,并将arg_lsit中的参数传递给callable。
此外,arg_lsit中的参数可能包含形式如_n的名字,其中n是个整数,这样的参数是“占位符”,是传递给newCallable的第n个参数。_n是定义在名为placeholders的命名空间中的,这个命名空间位于std空间中,所以_1对应的using声明为:
using std::placeholders::_1。
下面举一个使用bind函数替换lambda表达式的例子。比如有个存放string对象的vector列表:
std::vector<string> strList;
使用lambda表达式查找列表中字符串长度大于等于6的元素:
DWORD dwLen = 6;
find_if( strList.begin(), strList.end(), [dwLen](const string & str )
{ str.size() >= dwLen;}); // 使用lambda表达式
用lambda函数实现比较简单。
如果要用一个普通函数去比较字符串长度,如何去调用find_if呢?比如检测字符串长度的函数如下:
bool CheckStrLen( const string& str, DWORD dwLen )
{return str.size() >= dwLen;
}
因为给find_if传入的比较函数只有一个参数,而此处的比较函数有两个参数,所以没法直接传入。我们可以使用bind函数去构建一个新的函数:
DWORD dwLen = 6;
auto CheckStrLenBind = bind( CheckStrLen, _1, dwLen ); // 调用CheckStrLenBind,就会调用CheckStrLen,然后给CheckStrLen传递两个参数
此处以一个字符串作为例子,看看bind函数调用过程:
DWORD dwLen = 6;
auto CheckStrLenBind = bind( CheckStrLen, _1, dwLen );
string str = “hello”;
bool bRet = CheckStrLenBind( str ); // 此处相当于调用CheckStrLen(str, dwLen)
所以,调用find_if时可以这样写:
DWORD dwLen = 6;
auto CheckStrLenBind = bind( CheckStrLen, _1, dwLen );
find_if( strList.begin(), strList.end(), CheckStrLenBind);
find_if给CheckStrLenBind传入一个string类对象,然后CheckStrLenBind将这个string类对象作为第一个参数传给最终的函数CheckStrLen,同时将dwLen传给CheckStrLen。
也可以直接这样写:
find_if( strList.begin(), strList.end(), bind( CheckStrLen, _1, dwLen ));
4、std::bind和std::function配合使用
我们也可以将std::bind和std::function配合起来使用,这样所有的可调用对象均有了统一的操作方法。看下面的例子:
#include <iostream>
#include <functional> //std::cout
using namespace std;
using namespace std::placeholders; // adds visibility of _1, _2, _3,...class Test
{
public:int i = 0;void func(int x, int y){cout << x << " " << y << endl;}
};int main()
{Test obj; //创建对象function<void(int, int)> f1 = bind(&Test::func, &obj, _1, _2);f1(1, 2); //输出:1 2function< int &()> f2 = bind(&Test::i, &obj);f2() = 123;cout << obj.i << endl;//结果为 123return 0;
}