C++11的std::function和bind绑定器

本文围绕C++中的可调用对象展开,介绍了其定义和类型。重点阐述了可调用对象包装器std::function的基本用法、作为回调函数的示例等,还讲解了std::bind绑定器的基本用法、占位符使用,以及二者的配合使用,统一了可调用对象的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可调用对象

        在C++中,存在“可调用对象”这么一个概念。准确来说,可调用对象有如下几种定义:

                1、是一个函数指针

                2、是一个具有operator()成员函数的类对象(仿函数)

                3、是一个可转换为函数指针的类对象

                4、是一个类成员(函数)指针

#include <iostream>
using namespace std;

void func(void)
{}

struct Foo
{
    void operator()(void){}
};

struct Bar
{
    using fr_t = void(*)(void);

    static void func(void)
    {
        printf("%s %s %d\n", __FILE__, __func__, __LINE__);
    }

    operator fr_t(void)
    {
        return func;
    }
};

struct A
{
    int a_;

    void mem_func(void)
    {
        printf("%s %s %d\n", __FILE__, __func__, __LINE__);
    }
};

int main()
{
    void (*func_ptr)(void) = &func; ///1、函数指针
    func_ptr();

    Foo foo;
    foo();                          ///2、仿函数

    Bar bar;
    bar();                          ///3、可被转化为函数指针的类对象

    void (A::*mem_func_ptr)(void) = &A::mem_func;   ///4、类成员函数指针

    int A::*mem_obj_ptr = &A::a_;           ///类成员指针

    A aa;
    (aa.*mem_func_ptr)();
    aa.*mem_obj_ptr = 123;

    return 0;
}

        从上述可以看到,除了类成员指针之外,上面定义涉及的对象均为可以像一个函数那样做调用操作。

        在C++11中,像上面例子中的这些对象(func_ptr,foo,bar,mem_func_ptr,mem_obj_ptr)都被称为可调用对象。相对应的,这些对象的类型被统称为“可调用类型”。

        现在,C++11通过std::function和std::bind统一了可调用对象的各自操作。

可调用对象包装器---std::function

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

std::function的基本用法

#include <iostream>
#include <functional>
using namespace std;

void func(void)
{
    cout << __FUNCTION__ << endl;
}

class Foo
{
public:
    static int foo_func(int a)
    {
        cout << __FUNCTION__ << "(" << a << ") ->: ";
        return a;
    }
};

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

};

int main()
{
    function<void(void)> fr1 = func;        ///绑定一个普通函数
    fr1();

    ///绑定一个类的静态成员函数
    std::function<int(int)> fr2 = Foo::foo_func;
    cout << fr2(123) << endl;

    Bar bar;
    fr2 = bar;
    cout << fr2(123) << endl;

    return 0;
}

运行结果如下:

        从上面我们可以看到std::function的使用方法,当我们给std::function填入合适的函数签名(即一个函数类型,只需要包括返回值和参数表)之后,它就变成了一个可以容纳所有这一类调用方式的“函数包装器”。

std::function作为回调函数的示例

#include <iostream>
#include <functional>
using namespace std;

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

    void notify(void)
    {
        callback_();
    }

private:
    std::function<void()> callback_;
};

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

int main()
{
    Foo foo;
    A aa(foo);

    aa.notify();

    return 0;
}

        从上面的例子中可以看到,std::function可以取代函数指针的作用。因为它可以保存函数延迟执行,所以比较适合作为回调函数。

std::function作为函数入参示例

#include <iostream>
#include <functional>
using namespace std;

void call_when_even(int x, const std::function<void(int)>& f)
{
    if (!(x & 1))
    {
        f(x);
    }
}

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

int main()
{
    for(int i = 0; i < 10; i++)
    {
        call_when_even(i, output);
    }
    cout << endl;

    return 0;
}

        从上面的例子中可以看到,std::function比普通函数指针更灵活和便利。

std::bind绑定器

        std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。

        通俗来讲,它主要有两大作用:

        1、将可调用对象与其参数一起绑定成一个仿函数。

        2、将多元(参数个数为n,n>1)可调用对象转成一元或者(n-1)元可调用对象,即只绑定部分参数。

std::bind基本用法

#include <iostream>
#include <functional>
using namespace std;

void call_when_even(int x, const std::function<void(int)>& f)
{
    if (!(x & 1))
    {
        f(x);
    }
}

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

void output_add_2(int x)
{
    cout << x + 2 << " ";
}


int main()
{
    {
        auto fr = std::bind(output, std::placeholders::_1);
        for(int i = 0; i < 10; i++)
        {
            call_when_even(i, fr);
        }
        cout << endl;
    }

    {
        auto fr = std::bind(output_add_2, std::placeholders::_1);
        for(int i = 0; i < 10; i++)
        {
            call_when_even(i, fr);
        }
        cout << endl;
    }

    return 0;
}

        我们使用std::bind在函数外部通过绑定不同的函数,控制了最后的执行结果。

        我们使用auto fr保存std::bind的返回结果,因为我们不关心std::bind真正的返回类型(实际上std::bind的返回类型是一个stl内部定义的仿函数类型),只需要知道它是一个仿函数,可以直接赋值给一个std::function。当然,这里直接使用std::function类型来保存std::bind的返回值也是可以的。

        std::placeholders::_1是一个占位符,代表这个位置将在函数调用时,被传入的第一个参数所替代。

std::bind的占位符

#include <iostream>
#include <functional>
using namespace std;

void output(int x, int y)
{
    cout <<  x << " " << y << endl;
}

int main()
{
    ///输出1,2
    std::bind(output, 1, 2)();

    ///输出2,2
    std::bind(output, std::placeholders::_1, 2)(2);

    ///输出3,444
    std::bind(output, 3, std::placeholders::_1)(444);

    ///error,调用时没有第二个参数
    ///std::bind(output, 3, std::placeholders::_2)(333);

    ///调用时第一个参数被吞掉了
    ///输入1, 333
    std::bind(output, 1, std::placeholders::_2)(7, 333);

    ///输入7, 333
    std::bind(output, std::placeholders::_1, std::placeholders::_2)(7, 333);

    return 0;
}

        上面对std::bind的返回结果直接施以调用。可以看到,std::bind可以直接绑定函数的所有参数,也可以绑定部分参数。

        在绑定部分参数的时候,通过std::placeholders,来决定空位参数将会属于调用发生时的第几个参数。

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

#include <iostream>
#include <functional>
using namespace std;

class A
{
public:
    int i_ = 0;

    void output(int x, int y)
    {
        cout << x << " " << y << endl;
    }
};

int main()
{
    A a;

    std::function<void(int, int)> fr = std::bind(&A::output, &a, std::placeholders::_1, std::placeholders::_2);

    fr(1, 2);

    std::function<int& (void)> fr_i = std::bind(&A::i_, &a);

    fr_i() = 123;

    cout << a.i_ << endl;

    return 0;
}

        fr的类型是std::function<void(int, int)>。我们通过使用std::bind,将A的成员函数output的指针和a绑定,并转换为一个仿函数放入fr中存储。

        之后,std::bind将A的成员i_的指针和a绑定,返回的结果被放入std::function<int&(void)>中存储,并可以在需要时修改访问这个成员。

<think>好的,我现在需要帮助用户理解如何使用C++中的std::bind绑定成员函数。首先,我得回忆一下std::bind的基本用法相关知识。根据用户提供的引用内容,特别是引用[3]引用[4],里面提到了使用std::functionstd::bind来包装类成员函数的例子。 首先,成员函数普通函数不同,因为它们的第一个参数隐式地是类的实例指针(即this指针)。所以当用std::bind绑定成员函数时,需要明确指定该实例对象。用户的问题是如何绑定成员函数,可能需要传递一个对象实例作为第一个参数,或者使用占位符来延迟绑定。 接下来,查看引用[3]中的示例代码,其中有一个例子是将Plus类的成员函数plus_d绑定std::function对象上。代码中使用了std::bind(&Plus::plus_d, Plus(), _1, _2)。这里,第一个参数是成员函数的指针,第二个参数是Plus类的一个实例,后面是两个占位符,对应成员函数plus_d的参数。这说明在绑定成员函数时,需要提供对象实例作为第二个参数给std::bind,或者在调用时传入。 那问题来了,如果用户想要在绑定时固定对象实例,或者希望在调用时再传入对象?比如,如果使用占位符来代表对象实例,但这样可能不太常见,因为成员函数的第一个参数是隐式的。所以正确的做法可能是在std::bind的时候就将对象实例绑定进去,或者通过占位符在调用时传入。但根据C++的规则,成员函数需要通过对象来调用,所以通常需要在bind的时候绑定对象实例,或者传递指针/引用。 再看引用[4],其中提到std::bind的实现机制主要作用,可能更详细地解释了参数绑定的顺序。成员函数的指针需要对象实例结合,之后才是成员函数的参数。所以在语法上,std::bind的第一个参数是成员函数指针,第二个是对象实例,然后才是成员函数所需的参数,可以使用占位符。 用户可能需要一个具体的例子,比如如何将一个类的成员函数注册为另一个类的回调函数,如引用[1]引用[2]提到的场景。例如,类A有一个成员函数,需要被类B作为回调使用。这时,需要使用std::bind将类A的实例成员函数绑定,生成一个可调用对象,然后传递给类B。 总结步骤应该是:1. 包含必要的头文件,如<functional>;2. 使用&ClassName::MemberFunction获取成员函数指针;3. 在std::bind中传递对象实例(可以是临时对象、指针或引用);4. 使用占位符_1, _2等表示调用时的参数;5. 将生成的bind对象赋值给std::function以便存储调用。 需要注意的是,当绑定的对象实例是临时对象时,比如通过值传递,如Plus(),那么这个对象的生命周期需要被考虑,否则可能导致悬垂引用。如果对象是通过指针或引用传递的,用户需要确保在回调时对象仍然有效。 可能还需要解释占位符的位置,比如成员函数如果有两个参数,那么std::bind中的占位符从_1开始,因为对象实例已经绑定,剩下的参数对应成员函数的参数。 最后,根据用户提供的示例代码,应该构造一个类似的例子,展示如何正确使用std::bind绑定成员函数,并可能指出常见错误,比如忘记传递对象实例或占位符顺序错误。</think>### 使用 `std::bind` 绑定 C++ 成员函数 #### 核心步骤 1. **包含头文件**:确保包含 `<functional>` 头文件。 2. **语法规则**:成员函数指针需与对象实例绑定,参数通过占位符或固定值指定。 3. **绑定方式**: - 若需绑定特定对象实例(如固定对象),需在 `std::bind` 中显式传递。 - 若需延迟绑定对象(如调用时传入),需使用占位符 `_1` 表示对象参数。 #### 示例代码 ```cpp #include <iostream> #include <functional> class Calculator { public: double multiply(double a, double b) { return a * b; } }; int main() { Calculator calc; // 绑定成员函数:固定对象实例,参数通过占位符传递 std::function<double(double, double)> func = std::bind(&Calculator::multiply, &calc, std::placeholders::_1, std::placeholders::_2); std::cout << "3 * 4 = " << func(3, 4) << std::endl; // 输出 12 return 0; } ``` #### 代码解析 - **`std::bind` 参数顺序**: 1. **成员函数指针**:`&Calculator::multiply`。 2. **对象实例**:`&calc`(此处传递指针,确保对象生命周期有效)。 3. **成员函数参数**:使用占位符 `_1` `_2` 表示调用时的输入参数[^3][^4]。 - **注意事项**: - 若对象实例为临时对象(如 `std::bind(..., Calculator(), ...)`),需注意其生命周期。 - 占位符 `_1` 对应调用时的第一个参数,与成员函数参数顺序一致。 #### 扩展场景:延迟绑定对象实例 ```cpp // 定义可调用对象,要求调用时传入 Calculator 实例 std::function<double(Calculator*, double, double)> delayed_func = std::bind(&Calculator::multiply, std::placeholders::_1, // 对象实例占位符 std::placeholders::_2, // 第一个参数 std::placeholders::_3); // 第二个参数 Calculator calc2; std::cout << "5 * 6 = " << delayed_func(&calc2, 5, 6) << std::endl; // 输出 30 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值