C++中`std::function`和`std::bind`的详细解析

一、核心概念

1. std::function

  • 功能:通用函数包装器,可存储任意可调用对象
  • 特点
    • 类型擦除:统一处理函数指针、lambda、成员函数等
    • 运行时有少量性能开销
    • 声明时需要指定函数签名

2. std::bind

  • 功能:绑定参数生成新可调用对象
  • 特点
    • 参数绑定和参数顺序调整
    • 支持占位符(_1_2等)
    • 可绑定成员函数和对象指针

二、基础用法示例

1. std::function声明与使用

#include <functional>

// 声明函数类型
std::function<int(int, int)> math_op; 

// 绑定普通函数
int add(int a, int b) {
    return a + b; }
math_op = add;
cout << math_op(2, 3);  // 输出5

// 绑定lambda表达式
math_op = [](int x, int y) {
    return x * y; };
cout << math_op(2, 3);  // 输出6

// 绑定成员函数
class Calculator {
   
public:
    int sub(int a, int b) {
    return a - b; }
};
Calculator calc;
math_op = std::bind(&Calculator::sub, &calc, std::placeholders::_1, std::placeholders::_2);
cout << math_op(5, 2);  // 输出3

2. std::bind参数绑定

using namespace std::placeholders;

void print_sum(int a, int b, int c) {
   
    std::cout << a + b + c << '\n';
}

// 绑定固定参数
auto f1 = std::bind(print_sum, 10, _1, _2);
f1(20, 30);  // 输出60(10+20+30)

// 调整参数顺序
auto f2 = std::bind(print_sum, _3, _2, _1);
f2(10, 20, 30);  // 输出60(30+20+10)

// 绑定引用参数
int counter = 0;
auto inc = std::bind([](int& c) {
    c++; }, std::ref(counter));
inc();
cout << counter;  // 输出1

三、应用场景

1. 回调机制

class Button {
   
    std::function<void()> onClick;
public:
    void setCallback(std::function<void()> cb) {
   
        onClick = cb;
    }

    void click() {
   
        if(onClick) onClick();
    }
};

Button btn;
btn.setCallback([](){
    cout << "Button clicked!"; });
btn.click();

2. 策略模式

class DataProcessor {
   
    std::function<void(std::vector<int>&)> strategy;
public:
    void setStrategy(std::function<void(std::vector<int>&)> s) {
   
        strategy = s;
    }

    void process(std::vector<int>& data) {
   
        if(strategy) strategy(data);
    }
};

// 使用不同处理策略
DataProcessor dp;
dp.setStrategy([](auto& d) {
    /* 加密策略 */ });
dp.process(data);

3. 函数适配器

// 将成员函数适配为自由函数
class Logger {
   
public:
    void write(const string& msg) {
   
        cout << "[LOG] " << msg << endl;
    }
};

Logger logger;
auto logFunc = std::bind(&Logger::write, &logger, _1);

// 统一接口调用
void systemOutput(const string& msg, std::function<void(const string&)> output) {
   
    output("System: " + msg);
}

systemOutput("Started", logFunc);

四、高级用法

1. 组合绑定

using namespace std::placeholders;

double complex_calc(int a, double b, const string& c) {
   
    return a * b + stod(c);
}

auto bind_calc = std::bind(complex_calc, _2, 3.14, _1);
double result = bind_calc("9.8", 2);  // 2 * 3.14 + 9.8 = 16.08

2. 绑定重载函数

class Overloaded {
   
public:
    void func(int) {
    cout << "int\n"; }
    void func(double) {
    cout << "double\n"; }
};

Overloaded obj;
auto f = std::bind(static_cast<void(Overloaded::*)(int)>(&Overloaded::func), &obj, _1);
f(10);  // 输出int

3. 绑定智能指针

class Service {
   
public:
    void handleRequest(int id) {
    /* ... */ }
};

auto service = std::make_shared<Service>();
auto handler = std::bind(&Service::handleRequest, service, _1);

// 保证对象生命周期
handler(1001);
 

五、注意事项

1. 生命周期管理

std::function<void()> createCallback() {
   
    int local = 42;
    // return [&](){ cout << local; };  // 危险!局部变量将销毁
    return [=](){
    cout << local; };     // 正确:值捕获
}

2. 性能考量

  • std::function调用比直接函数调用慢约2-3倍
  • 小对象建议使用lambda,大对象使用std::ref

3. 类型安全

std::function<void(int)> f = [](string s){
   };  // 编译错误!参数类型不匹配
 

六、与Lambda表达式对比

特性 std::bind Lambda表达式
参数绑定 显式使用placeholders 自动捕获外部变量
成员函数绑定 需要显式指定对象指针 捕获this即可
可读性 复杂参数绑定时较难理解 结构清晰直观
闭包大小 通常较大(存储绑定参数) 由捕获变量决定
内联优化 较少优化机会 编译器更容易优化

推荐实践

// 优先使用Lambda
auto f = [x](int a, int b) {
    return x + a * b; };

// 需要复杂参数绑定时使用bind
auto bound = std::bind(&Class::method, obj_ptr, _1, 42);
 

七、现代C++改进

1. 通用Lambda(C++14)

auto generic_lambda = [](auto x, auto y) {
    return x + y; };
std::function<int(int,int)> f = generic_lambda;

2. 模板参数推导(C++17)

std::function f = [](){
    /* ... */ };  // C++17起支持推导

3. std::invoke(C++17)

template<typename Callable>
void execute(Callable&& c) {
   
    std::invoke(std::forward<Callable>(c));
}
posted @ 2025-11-01 17:17  u95900090  阅读(17)  评论(0)    收藏  举报