std::bind的讲解

一、在讲解std::bind之前,我们先来复习下std::function。

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

什么是可调用对象?

(1)函数指针
(2)具有operator()成员函数的类对象(传说中的仿函数),lambda表达式
(3)可被转换为函数指针的类对象
(4)类成员(函数)指针
(5)bind表达式或其它函数对象

比如:

// 普通函数
int add(int a, int b){return a+b;} 

// lambda表达式
auto mod = [](int a, int b){ return a % b;}

// 函数对象类
struct divide{
    int operator()(int denominator, int divisor){
        return denominator/divisor;
    }
};

用std::function将上述类型保存起来:

std::function<int(int ,int)>  a = add; 
std::function<int(int ,int)>  b = mod ; 
std::function<int(int ,int)>  c = divide(); 

二、std::bind

可将std::bind函数看作一个通用的函数适配器,它接受一个“可调用对象”,生成一个新的可调用对象来“适应”原对象的参数列表。

std::bind主要有以下两个作用:
(1)将可调用对象和其参数绑定成一个防函数;
(2)只绑定部分参数,减少可调用对象传入的参数。

1、绑定普通函数

double divide (double x, double y) {
    return x/y;
}

auto divide_half = std::bind (my_divide,_1,2);  
std::cout << fn_half(10) << std::endl; // 输出5

(1)bind的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。因此std::bind (divide,_1,2)等价于std::bind (&divide,_1,2);
(2)_1表示占位符,位于<functional>中,std::placeholders::_1;

2、绑定成员函数

#include <functional>
#include <iostream>

using namespace std;

struct Foo {
    void sum(int n1, int n2)
    {
        std::cout << n1+n2 << std::endl;
    }

	static int StaticMember(int a) { 
        return 66 + a; 
    }

    int data = 10;
};

int main() {
    Foo foo;
    auto f = std::bind(&Foo::sum, &foo, 95, std::placeholders::_1);
    f(5); // 输出100
}

(1)bind绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址。
(2)必须显示的指定&Foo::sum,因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在Foo::sum前添加&;
(3)使用对象成员函数的指针时,必须要知道该指针属于哪个对象,因此第二个参数为对象的地址 &foo;

3、绑定静态成员函数

	auto fun2 = &Foo::StaticMember;
    cout<<fun2(3)<<endl;

4、绑定成员变量

#include <functional>
#include <iostream>

using namespace std;

class TestClass4 {
public:
    TestClass4() : m_a(100)
    {}
	
public:
    int m_a;
};

int main() {
    TestClass4 test4;
    auto fun4 = std::bind(&TestClass4::m_a, std::placeholders::_1);

	int var = fun4(test4);
    std::cout<<var<<std::endl;
}

5、绑定仿函数

#include <functional>
#include <iostream>

using namespace std;

class TestClass5 {
public:
    TestClass5() = default;
	
    TestClass5(const TestClass5& obj) {
        std::cout<<"TestClass5 copy construct."<<std::endl;
    }
	
    void operator()(int a) {
        std::cout<<a<<std::endl;
    }
};

int main(){
    TestClass5 test5;
    auto fun5 = test5;
    fun5(2018);
}

6、绑定成员函数时,哪些做法会先调用“拷贝构造函数” ?

#include <functional>
#include <iostream>

using namespace std;

class TestClass {
public:
    TestClass(int a):m_a(a){}
    TestClass(const TestClass& obj) {
        m_a = obj.m_a + 100;
        std::cout<<"copy construct."<<std::endl;
    }
	
    int ClassMember(int a) {
	    std::cout<<" this:"<<this<<" :"<<&m_a<<" "<<m_a<<std::endl;
		return 55 + a; 
	}
	
    int ClassMember2(int a,char ch,float f) {
        std::cout <<ch <<" "<< f << " "<<a<<std::endl;
        return 55 + a;
    }
	
    static int StaticMember(int a) {
	    return 66 + a; 
	}

public:
    int m_a;
};;

int main(){
    TestClass test(67);
    std::cout<<"&test "<<&test<<" "<<test.m_a<<" &test.m_a "<<&test.m_a<<std::endl;
    std::cout<<"---------------------------------------------------------------------"<<std::endl;
	auto fun4 = std::bind(&TestClass::ClassMember, test, std::placeholders::_1); // 调用拷贝构造函数
	fun4(4);
	
	std::cout<<"---------------------------------------------------------------------"<<std::endl;
	auto fun5 = std::bind(&TestClass::ClassMember, &test, std::placeholders::_1); // 不调用拷贝构造函数
    fun5(5);
	
	std::cout<<"---------------------------------------------------------------------"<<std::endl;
	auto fun6 = &TestClass::StaticMember; // 不调用拷贝构造函数
    std::cout<<fun6(6)<<std::endl;
	
	std::cout<<"---------------------------------------------------------------------"<<std::endl;
    auto fun7 = std::bind(&TestClass::m_a, std::placeholders::_1); // 不调用拷贝构造函数
    std::cout<<fun7(test)<<" "<<test.m_a<<std::endl;
	
}

执行结果:

7、传引用参数的问题

当需要把对象传到bind中的参数中时,需要使用ref或者cref。

#include<iostream>
#include<functional>
 
using namespace std;
using namespace placeholders;
 
void alter(int &a, int b) {
    a = b;
}

int main() {
    int a = 5;
    auto f1 = bind(alter, a, _1);      //此处bind中的参数a只是a的一个拷贝,而不是a的引用
    f1(9);                           
    cout << a << endl;                //所以此处a仍为5
 
    auto f2 = bind(alter, ref(a), _1);  //使用ref可以将a的引用传递给bind
    f2(9);
    cout << a << endl;                //所以此处a变成了9
 
    return 0;
}
 
 

8、指向成员函数的指针

#include <iostream>
struct Foo {
    int value;
    void f1() { std::cout << "f1(" << this->value << ")\n"; }
    void f2() { std::cout << "f2(" << this->value << ")\n"; }
};

void apply(Foo* foo1, Foo* foo2, void (Foo::*fun)()) {
    (foo1->*fun)();  // call fun on the object foo1
    (foo2->*fun)();  // call fun on the object foo2
}

int main() {
    Foo foo1{1};
    Foo foo2{2};

    apply(&foo1, &foo2, &Foo::f1);
    apply(&foo1, &foo2, &Foo::f2);
}

(1)成员函数指针的定义:void (Foo::*fun)(),调用时传递的实参: &Foo::f;

(2)fun为类成员函数指针,所以调用时要通过解引用的方式获取成员函数*fun,即(foo1->*fun)()。

参考:

(1)C++11 中的std::function和std::bind

(2)c++bind函数使用

(3)第六节 std::bind 绑定器

(4)c++11随记:std::bind及 std::placeholders

<think>好的,用户之前问了关于现代C++中的函数回调机制,现在他们想更详细地了解`std::bind`的用法。我需要先回想之前的回答中提到的`std::bind`部分,确保现在的内容不重复但深入展开。 用户可能已经知道`std::bind`用于绑定成员函数或调整参数顺序,但需要具体的使用示例和细节。我需要解释`std::bind`的基本用法、占位符的作用、如何处理成员函数和对象,以及与其他方法如Lambda的对比。 要注意用户可能不太清楚`std::bind`和Lambda之间的选择,所以需要比较两者的优缺点。同时,要提醒常见的错误,比如绑定对象生命周期的问题,以及占位符顺序的重要性。 可能需要提供多个代码示例,覆盖不同的应用场景,比如参数重排、部分绑定、组合函数等。还要注意语法细节,比如`std::placeholders::_1`的使用和头文件的引入。 另外,用户可能希望了解何时使用`std::bind`而不是Lambda,特别是在需要兼容C++11之前版本或者需要更复杂的参数绑定时。需要强调`std::bind`在C++11中的角色,以及在C++14之后Lambda可能更受欢迎的情况。 最后,确保回答结构清晰,分点讲解,每个部分都有代码示例和解释,帮助用户逐步理解`std::bind`的用法和适用场景。</think>以下是关于`std::bind`的详细用法解析,包含基本概念、核心场景和代码示例: --- ### **`std::bind` 核心功能** C++11引入的函数适配器,用于**部分参数绑定**或**参数顺序调整**,将可调用对象与其参数绑定,生成新的可调用对象。需包含头文件 `<functional>`。 --- ### **基本语法** ```cpp #include <functional> auto bound_fn = std::bind( OriginalFunction, arg1, arg2, ..., argN ); ``` - **参数规则**: - 若参数是具体值(如`42`, `obj`),则绑定时固定该值 - 若参数是占位符(`std::placeholders::_1`, `_2`等),表示调用时动态传入 --- ### **核心应用场景** --- #### 1. **绑定普通函数** ```cpp void print_sum(int a, int b) { std::cout << a + b; } // 绑定参数:固定a=10,b由调用时传入 auto bind_sum = std::bind(print_sum, 10, std::placeholders::_1); bind_sum(20); // 输出30(10+20) ``` --- #### 2. **绑定成员函数** 需显式绑定对象实例(需注意对象生命周期): ```cpp class Calculator { public: int multiply(int a, int b) { return a * b; } }; Calculator calc; // 绑定对象实例 + 参数 auto bound_mul = std::bind( &Calculator::multiply, &calc, std::placeholders::_1, // a参数 std::placeholders::_2 // b参数 ); std::cout << bound_mul(5, 6); // 输出30(5*6) ``` --- #### 3. **参数重排** 调整参数顺序(如适配接口不匹配的场景): ```cpp void log(int id, const std::string& msg) { std::cout << id << ": " << msg; } // 将参数顺序调换:msg变为第一个参数,id固定为100 auto reordered = std::bind(log, 100, std::placeholders::_1); reordered("Hello"); // 输出 "100: Hello" ``` --- #### 4. **部分参数绑定(Currying)** 固定部分参数,生成新函数: ```cpp double power(double base, double exponent) { return std::pow(base, exponent); } // 固定base=2,生成平方函数 auto square = std::bind(power, 2.0, std::placeholders::_1); std::cout << square(3); // 输出8(2^3) // 固定exponent=3,生成立方函数 auto cube = std::bind(power, std::placeholders::_1, 3.0); std::cout << cube(3); // 输出27(3^3) ``` --- ### **占位符(Placeholders)** - 定义在命名空间 `std::placeholders` 中 - `_1` 表示新函数的第一个参数,`_2` 表示第二个,以此类推 - **占位符顺序决定参数传递逻辑**: ```cpp auto fn = std::bind(func, _2, _1); // 调用时:fn(a,b) → func(b,a) ``` --- ### **与Lambda的对比** | **特性** | `std::bind` | **Lambda** | |-------------------|--------------------------------------|-------------------------------------| | **参数绑定** | 显式使用占位符和固定值 | 通过捕获列表隐式或显式捕获变量 | | **可读性** | 复杂参数绑定时较难理解 | 直接显示操作逻辑,更直观 | | **性能** | 可能有间接调用开销 | 可内联优化(编译器依赖) | | **适用场景** | 需要兼容旧代码或复杂参数重排 | 简单逻辑、需捕获上下文变量时更简洁 | --- ### **常见错误示例** #### 错误1:对象生命周期问题 ```cpp std::function<void()> create_bind() { Calculator local_calc; // 绑定局部对象,离开作用域后悬空引用! return std::bind(&Calculator::multiply, &local_calc, 2, 3); } ``` #### 错误2:占位符顺序混淆 ```cpp void func(int a, int b, int c); // 绑定后调用 bound(1,2,3) → 实际传递 c=1, a=2, b=3 auto bound = std::bind(func, _3, _1, _2); ``` --- ### **进阶用法:嵌套绑定** 组合多个绑定函数(需谨慎使用,可能降低可读性): ```cpp auto add = [](int a, int b) { return a + b; }; auto bound_add = std::bind(add, _1, 10); // 固定第二个参数为10 auto multiply_and_add = std::bind( add, std::bind(bound_add, _1), // 嵌套绑定:bound_add(x) + 20 20 ); std::cout << multiply_and_add(5); // 输出35:(5+10) + 20 ``` --- ### **总结** - **适用场景**:适配旧接口、参数重排、部分参数固定 - **替代方案**:C++11后优先考虑Lambda(更直观),但`std::bind`在复杂参数操作中仍有价值 - **注意点**:对象生命周期管理、占位符顺序逻辑
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值