C++编程经验(11):std::function 和 bind绑定器

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++编程经验(11):std::function 和 bind绑定器相关的知识,希望对你有一定的参考价值。

简介

在前面C++集群的项目里面大量应用到了绑定器来做解耦操作,那么,绑定器到底是什么呢?有什么玄妙的地方嘞?

其实也不是很玄乎,以前写Qt的时候就经常用到绑定,昨天又发现,其实我们一直在用绑定器却不自知,比如说创建线程,将函数指针与它的参数一并传入。


std::function

在这一篇博客里(C++搭建集群聊天室(八):网络层代码与业务层代码(登录注册)解耦),我写过这样的代码:

#include <functional>
···
using MsgHandler = std::function<void(const TcpConnectionPtr &conn,json &js,Timestamp time)>;
···
MsgHandler getHandle(int msgid);
···
unordered_map<int,MsgHandler> _msgHanderMap;
···
//注册消息以及对应的回调操作
ChatService::ChatService(){
    _msgHanderMap.insert({LOGIN_TYPE,std::bind(&ChatService::login,this,_1,_2,_3)});
    _msgHanderMap.insert({REG_TYPE,std::bind(&ChatService::reg,this,_1,_2,_3)});
}

要怎么去界定这个MsgHandler?或者换句话说,要怎么理解它?看成一个自定义数据类型?一个typename?那它到底是type了谁的name???

可调用对象

在C++中,有“可调用对象”这么个概念:

函数指针;
具有operator()成员函数的类对象(仿函数);
可以被转换为函数指针的类对象;
类成员(函数)指针。

std::function是一个可调用对象的包装器,一个类模板,可以容纳除了类成员(函数)指针之外的所用可调用对象,通过指它的模板参数,可以以统一的方式处理函数、函数对象、函数指针,并允许保存或者延迟执行。

使用方法:

#include <iostream>
#include <functional>

void func1(void)
{
    std::cout << __FUNCTION__ << std::endl;
}

class Test
{
public:
    static int func2(int a)
    {
        std::cout << __FUNCTION__ << "(" << a << ")->: ";
        return a;
    }
};

class Test2
{
public:

    int operator()(int a)
    {
        std::cout << __FUNCTION__ << "(" << a << ")->: ";
        return a;
    }
};

int main(void)
{
    //绑定一个普通函数
    std::function<void(void)> fb1 = func1;
    fb1();

    //绑定一个静态成员函数
    std::function<int(int)> fb2 = Test::func2;
    std::cout << fb2(123) << std::endl;

    //绑定一个仿函数
    Test2 bar;
    fb2 = bar;
    std::cout << fb2(456) << std::endl;

    return 0;
}
func1
Test::func2(123)->: 123
Test2::operator ()(456)->: 456

接下来来看看熟悉的场景,使用 function 做回调。

#include <iostream>
#include <functional>

class A
{
    std::function<void()> callback;
public:
    A(const std::function<void()>& f) : callback(f) {}

    void notify(void)
    {
        callback();
    }
};

class B
{
public:
    void operator()(void)
    {
        std::cout << __FUNCTION__ << std::endl;
    }
};

int main(void)
{
    B b;
    A a(b);
    a.notify();

    return 0;
}

稍微有点抽象,但也不是说很抽象啊。

B::operator ()

接下来,再看利用function做函数指针:

#include <iostream>
#include <functional>

using namespace std;

void run(int x, const std::function<void(int)>& f)
{
    if (x % 2 != 0) 
    {
        f(x);
    }
}

void func(int x)
{
    cout << x << "  ";
}

int main(void)
{
    for (int i = 0; i < 10; i++)
    {
        run(i, func);
    }

    cout << std::endl;

    return 0;
}

function的部分且先讲到这里,单看一个function,其实没什么特别突出的地方,甚至写的还麻烦了。


std::bind

std::bind用来将可调用对象与起参数一起进行绑定,绑定的结果使用std::function进行保存,并在我们需要调用的时候调用。

它主要有两大作用:

将可调用对象和参数绑定成为一个仿函数;
将多元(参数个数为n,n-1)可调用对象转换成一元或者(n-1)元可调用对象,即只绑定部分对象。

来看一下用法示例:
在前面原有代码的基础上做一下函数参数的扩充:

#include <iostream>
#include <functional>

void run(int x, const std::function<void(int)>& f)
{
    if (!(x & 1)) //x % 2 == 0
    {
        f(x);
    }
}

void func1(int x)
{
    std::cout << x << "  ";
}

void func2(int x)
{
    std::cout << x + 2 << "  ";
}

int main(void)
{
    auto fr = std::bind(func1, std::placeholders::_1);
    for (int i = 0; i < 10; i++)
    {
        run(i, fr);
    }

    std::cout << std::endl;
    return 0;
}

联想一下 pthread_create 函数,有没有种熟悉的感觉、


std::placeholders

这个呢,之前在项目博客里说过,是占位符。

通过std::placeholders占位符绑定函数参数,使得std::bind的使用非常灵活。std::placeholders决定函数占用位置取用输入参数的第几个参数。

using namespace placeholders;

···
    _server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));

···
    _server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));


以上是关于C++编程经验(11):std::function 和 bind绑定器的主要内容,如果未能解决你的问题,请参考以下文章

C++ 11 std::function std::bind使用

C++11 std::function用法(c++常问问题十七)

如何使用 pybind11 将 python 函数转换为 std::function

muduo库中的核心:std::bind和std::function

C++ 中的 GUI 工具包出现问题。将 lambda 传递到 std::function 的向量中

带有非 const 字符串的 C++ lambda 到 std::function 错误