关于callback和std::bind的那些事

发布时间 2023-08-16 17:58:29作者: azureology

前言

使用callback常常需要绑定类的具体函数,哪些可以绑定哪些不能?

分析

callback不同与普通函数,其入参也是一个函数,具体行为由入参决定
我们看这样一段代码
https://godbolt.org/z/4YTKs567j

#include <functional>
#include <iostream>

class A
{
public:
void print(const int& m)
{
    std::cout << "A::print: " << m << std::endl;
}
};

class B
{
public:
using CallbackType = std::function<void(const int&)>;
void set(const CallbackType& f)
{
    function = f;
}
void run(const int& n)
{
    if(function)
    {
        function(n);
    }
}
private:
    CallbackType function = nullptr;
};

void outPrint(const int& m)
{
    std::cout << "outPrint: " << m << std::endl;
}

int main()
{
    A a{};
    B b{};

    b.set(outPrint);
    b.run(8);
    // b.set(a.print); // won't compile
    b.set(std::bind(&A::print,&a,std::placeholders::_1));
    b.run(7);
    return 0;
}

运行结果

ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
outPrint: 8
A::print: 7

其中outPrint()作为外部函数很容易被掉用,比较好理解。
若尝试传入a.print()会报错

<source>: In function 'int main()':
<source>:44:18: error: invalid use of non-static member function 'void A::print(const int&)'
     b.set(a.print); // won't compile
                  ^
<source>:7:6: note: declared here
 void print(const int& m)
      ^~~~~
Compiler returned: 1

一般看到这里会把A::print()注册为static方法使其脱离A成为独立函数,如果A::print()适用了A中其他变量也都需要声明为static类型,也许这能够解决问题,但本质上不能传入的原因是因为A::print()需要两个入参,第一个为编译器隐式传入的thisz指针,因此可以使用std::bind固化部分参数完成函数特化。

总结

对于无法callback的报错不要盲目修改static尽量适用std::bind的方式对函数进行特化减少入参。