C++11新特性④ | 模板类std::function和标准库函数std::bind

目录

1、引言

2、std::function函数模板类

3、std::bind标准库函数

4、std::bind和std::function配合使用


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章正在更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://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。

扫描二维码关注公众号,回复: 16546363 查看本文章
#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;

    /*
     运行结果:
        func
        foo_func(111) ->: 111
        operator()(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 2

    function< int &()> f2 = bind(&Test::i, &obj);
    f2() = 123;
    cout << obj.i << endl;//结果为 123

    return 0;
}

猜你喜欢

转载自blog.csdn.net/chenlycly/article/details/132774379