通俗的来讲function是函数包装器而bind绑定器的作用是将二元或者多元的仿函数降低维度。C11 引入的std::function和std::bind是函数式编程的核心组件彻底改变了 C 中可调用对象的处理方式。它们解决了传统 C 中可调用对象类型不统一、参数无法灵活适配的问题是实现回调、策略模式、延迟调用的基础。一、核心概念std::function可调用对象的类型统一器。它是一个模板类能够包装任何可调用对象普通函数、函数指针、lambda、仿函数、成员函数指针并将其转换为具有统一签名的类型。std::bind参数适配器。它是一个函数模板能够将一个可调用对象与部分参数绑定生成一个新的可调用对象。支持固定参数值、调整参数顺序、延迟调用等功能。二、std::function 详解2.1 基本语法#include functional // 模板参数格式返回值类型(参数类型列表) std::function返回值(参数1类型, 参数2类型, ...) func;2.2 包装所有可调用对象std::function最大的价值在于类型擦除它能将不同类型但签名相同的可调用对象统一为同一个std::function类型。#include iostream #include functional // 1. 普通函数 int add(int a, int b) { return a b; } // 2. 仿函数函数对象 struct Sub { int operator()(int a, int b) const { return a - b; } }; // 3. 类的普通成员函数 class Calculator { public: int mul(int a, int b) const { return a * b; } static int div(int a, int b) { return a / b; } }; int main() { // 包装普通函数 std::functionint(int, int) func1 add; std::cout func1(2, 3) std::endl; // 输出5 // 包装仿函数 std::functionint(int, int) func2 Sub(); std::cout func2(5, 2) std::endl; // 输出3 // 包装lambda表达式 std::functionint(int, int) func3 [](int a, int b) { return a % b; }; std::cout func3(7, 3) std::endl; // 输出1 // 包装静态成员函数无需绑定对象 std::functionint(int, int) func4 Calculator::div; std::cout func4(6, 2) std::endl; // 输出3 // 包装普通成员函数必须绑定对象实例见下文bind部分 Calculator calc; std::functionint(int, int) func5 std::bind(Calculator::mul, calc, std::placeholders::_1, std::placeholders::_2); std::cout func5(3, 4) std::endl; // 输出12 return 0; }2.3 核心特性空值检查std::function可以为空调用空的std::function会抛出std::bad_function_call异常。std::functionvoid() empty_func; if (empty_func) { empty_func(); // 不会执行 }赋值与拷贝支持拷贝构造、移动构造和赋值操作可作为函数参数和返回值传递。容器存储由于类型统一可将多个不同类型的可调用对象存入同一个容器。std::vectorstd::functionint(int, int) ops; ops.push_back(add); ops.push_back(Sub()); ops.push_back([](int a, int b) { return a * b; });三、std::bind 详解3.1 基本语法#include functional // 绑定可调用对象和参数 auto new_func std::bind(可调用对象, 参数1, 参数2, ...);参数可以是固定值也可以是占位符std::placeholders::_1,_2, ...,_N。占位符代表新可调用对象的第 N 个参数。例如_1表示新函数的第一个参数_2表示第二个参数。返回值一个未指定类型的可调用对象通常用auto接收或用std::function包装。3.2 核心用法用法 1绑定固定参数柯里化将原函数的部分参数固定为常量生成一个参数更少的新函数。#include iostream #include functional int add(int a, int b) { return a b; } int main() { // 绑定第一个参数为10生成一个只需要1个参数的新函数 auto add10 std::bind(add, 10, std::placeholders::_1); std::cout add10(5) std::endl; // 等价于 add(10,5) → 15 std::cout add10(20) std::endl; // 等价于 add(10,20) → 30 // 绑定两个参数生成无参函数 auto add5_3 std::bind(add, 5, 3); std::cout add5_3() std::endl; // 等价于 add(5,3) → 8 return 0; }用法 2调整参数顺序通过占位符的顺序改变新函数参数的传递顺序。#include iostream #include functional int divide(int a, int b) { return a / b; } int main() { // 原函数divide(被除数, 除数) // 新函数reverse_divide(除数, 被除数) auto reverse_divide std::bind(divide, std::placeholders::_2, std::placeholders::_1); std::cout divide(10, 2) std::endl; // 10/25 std::cout reverse_divide(2, 10) std::endl; // 10/25 return 0; }用法 3绑定类的成员函数类的普通成员函数有一个隐含的this指针参数因此绑定成员函数时第一个参数必须是对象实例或指针、引用。#include iostream #include functional class Person { public: void say_hello(const std::string name) const { std::cout Hello, name ! Im m_name std::endl; } std::string m_name; }; int main() { Person p{Alice}; // 绑定成员函数和对象实例 auto say_hello_to std::bind(Person::say_hello, p, std::placeholders::_1); say_hello_to(Bob); // 等价于 p.say_hello(Bob) // 也可以绑定对象本身值传递会拷贝对象 auto say_hello_to_copy std::bind(Person::say_hello, p, std::placeholders::_1); p.m_name Charlie; say_hello_to_copy(Bob); // 输出 Hello, Bob! Im Alice拷贝的对象未改变 return 0; }用法 4传递引用参数std::bind默认对参数进行值传递。如果需要传递引用必须使用std::ref左值引用或std::crefconst 左值引用。#include iostream #include functional void increment(int x) { x; } int main() { int num 0; // 错误bind默认值传递会拷贝num无法修改原变量 // auto bad_inc std::bind(increment, num); // bad_inc(); // std::cout num std::endl; // 输出0 // 正确使用std::ref传递引用 auto good_inc std::bind(increment, std::ref(num)); good_inc(); std::cout num std::endl; // 输出1 return 0; }四、std::function 与 std::bind 组合使用这是最常见的用法用std::bind生成适配后的可调用对象再用std::function统一类型实现回调机制和策略模式。示例实现一个简单的事件处理器#include iostream #include functional #include vector // 事件类型 enum class Event { Click, KeyPress }; // 事件处理器统一接收Event类型的回调 class EventHandler { public: using Callback std::functionvoid(Event); void register_callback(Callback cb) { callbacks_.push_back(cb); } void trigger_event(Event e) { for (auto cb : callbacks_) { cb(e); } } private: std::vectorCallback callbacks_; }; // 不同签名的回调函数 void on_event(Event e, const std::string name) { std::cout name received event: static_castint(e) std::endl; } class Logger { public: void log_event(Event e) { std::cout Logger: Event static_castint(e) occurred std::endl; } }; int main() { EventHandler handler; // 绑定普通函数的额外参数 handler.register_callback(std::bind(on_event, std::placeholders::_1, App1)); handler.register_callback(std::bind(on_event, std::placeholders::_1, App2)); // 绑定成员函数 Logger logger; handler.register_callback(std::bind(Logger::log_event, logger, std::placeholders::_1)); // 触发事件 handler.trigger_event(Event::Click); return 0; }五、常见应用场景回调函数异步编程、事件驱动架构如 GUI、网络编程中将回调函数注册给框架。策略模式运行时动态切换算法无需继承体系。参数适配将不同签名的函数适配成统一接口满足框架要求。延迟调用将函数和参数打包在合适的时机执行如线程池任务。函数柯里化将多参数函数分解为多个单参数函数便于函数组合。六、注意事项与最佳实践优先使用 lambda 表达式C11 之后lambda 表达式在大多数场景下比std::bind更简洁、易读、灵活。特别是复杂的参数绑定和逻辑处理lambda 的优势明显。// bind版本 auto add10_bind std::bind(add, 10, std::placeholders::_1); // lambda版本更清晰 auto add10_lambda [](int x) { return add(10, x); };避免绑定临时对象如果绑定的对象是临时对象当临时对象销毁后调用绑定后的函数会导致未定义行为。成员函数绑定注意对象生命周期绑定成员函数时必须保证对象的生命周期长于绑定后的可调用对象。空值检查调用std::function前务必检查是否为空避免抛出异常。性能考量std::function有轻微的运行时开销类型擦除和虚函数调用但在绝大多数场景下可以忽略不计。