C++ 可调用对象包装器:function 与 bind

张开发
2026/4/16 1:44:14 15 分钟阅读

分享文章

C++ 可调用对象包装器:function 与 bind
一、什么是可调用对象在 C 中“可调用对象”指的是可以像函数一样被调用的对象。主要有以下几种类型示例函数指针void (*p)()仿函数重载了operator()的类对象Lambda 表达式[](int x){ return x; }类成员函数指针MyClass::funcstd::function 包装的对象std::functionvoid() f func;二、函数指针函数指针是最传统的可调用对象本质是指向函数的指针。#include iostream using namespace std; int add(int a, int b) { return a b; } int sub(int a, int b) { return a - b; } int main() { // 定义函数指针返回值 (*指针名)(参数类型) int (*pf)(int, int) nullptr; // 指向 add 函数 pf add; // 函数名就是地址等价于 pf add; cout pf(10, 20) endl; // 30 // 指向 sub 函数 pf sub; cout pf(10, 20) endl; // -10 return 0; }说明函数名就是函数的地址所以pf add和pf add都可以。函数指针可以指向不同类型的函数只要返回值类型和参数列表匹配。三、仿函数仿函数是一个重载了operator()的类或结构体它的对象可以像函数一样调用。class Add { private: int value; public: Add() : value(0) {} int operator()(int a, int b) { value a b; return value; } int operator()(int a) { value a * a; return value; } void Print() const { cout value: value endl; } }; int main() { Add add; int c add(10, 20); // 调用 operator()(int, int) // 等价于 add.operator()(10, 20) add.Print(); // value: 30 c add(5); // 调用 operator()(int) // 等价于 add.operator()(5) add.Print(); // value: 25 return 0; }为什么add(10, 20)能像函数一样调用因为Add类重载了operator()add(10, 20)是语法糖编译器会转换成add.operator()(10, 20)。这让对象可以像函数一样被调用所以称为“仿函数”。特点特点说明可以有多个重载版本参数不同调用时自动匹配可以保存状态有成员变量多次调用可以记住之前的值可以作为算法的参数如sort的自定义比较函数四、std::function 可调用对象包装器问题为什么需要 std::function不同类型的可调用对象类型不一样可调用对象类型普通函数void(*)()仿函数Foo自定义类型Lambda编译器生成的唯一类型无法写出void func() {} struct Foo { void operator()() {} }; auto lam []() {};三个变量类型不同无法放在同一个数组里std::function 的作用把不同类型的可调用对象包装成统一的类型。std::functionvoid() f; // 统一类型 f func; // 可以存普通函数 f Foo(); // 可以存仿函数 f [](){}; // 可以存 Lambda4.1 基本用法#include functional #include iostream using namespace std; void func() { cout 全局函数 endl; } struct Foo { void operator()() { cout 仿函数 endl; } }; struct Bar { static void static_func() { cout 静态成员函数 endl; } }; int main() { // std::functionvoid(void) 表示包装一个返回 void、无参数的函数 std::functionvoid(void) f; f func; // 包装全局函数 f(); // 输出全局函数 f Bar::static_func; // 包装静态成员函数 f(); // 输出静态成员函数 Foo foo; f foo; // 包装仿函数 f(); // 输出仿函数 return 0; }语法解释std::functionvoid(void)中的void(void)表示第一个void返回值类型括号内的void参数列表无参数std::functionvoid(void)表示包装一个返回 void、无参数的函数4.2 为什么需要 std::function场景想把多个不同类型的可调用对象放进一个数组统一调用。std::functionvoid(void) funcList[3]; funcList[0] func; // 普通函数 funcList[1] foo; // 仿函数 funcList[2] []() { cout Lambda endl; }; // Lambda for (auto f : funcList) { f(); // 统一调用 }没有 std::function 的话三种类型不同无法放在同一个数组里。一句话总结std::function是一个“万能容器”可以把函数指针、仿函数、Lambda 等统一装进去然后统一调用。五、std::bind 绑定器std::bind用于将函数的参数绑定到固定值生成一个新的可调用对象。5.1 基本用法#include functional #include iostream using namespace std; void output(int a, int b) { cout a a , b b endl; } int main() { int x 10, y 20; // 两个参数都固定 auto f1 std::bind(output, x, y); f1(); // 输出a 10, b 20 return 0; }说明std::bind(output, x, y)把output的两个参数都固定为x和y生成的新函数f1不需要再传参。5.2 占位符_1、_2占位符表示参数在调用时才传入。#include functional using namespace std::placeholders; // _1, _2 在这个命名空间 int main() { int x 10; // 第一个参数固定第二个参数调用时传入 auto f2 std::bind(output, x, _1); f2(12); // a 10, b 12 // 第一个参数固定第二个参数用占位符 _2只绑定第二个实参 auto f3 std::bind(output, x, _2); f3(1, 2); // a 10, b 2_2 取第二个参数 2 // 参数顺序交换 auto f4 std::bind(output, _2, _1); f4(1, 2); // a 2, b 1_2 取第二个参数_1 取第一个参数 return 0; }说明_1表示调用时的第一个参数_2表示第二个参数。通过占位符可以固定部分参数或重新排列参数顺序。5.3 占位符说明占位符含义_1调用时的第一个参数_2调用时的第二个参数_3调用时的第三个参数// bind 时定义参数顺序调用时传入实参 auto f std::bind(output, _2, _1, _3); f(10, 20, 30); // 实际调用 output(20, 10, 30)说明占位符可以任意重排参数顺序_1永远是调用时的第一个参数_2是第二个以此类推。5.4 绑定成员函数class Int { private: int value; public: Int(int x 0) : value(x) {} void print() const { cout value value endl; } }; int main() { Int a(10); // 绑定成员函数需要传递对象指针 std::functionvoid(void) f std::bind(Int::print, a); f(); // 输出value 10 return 0; }说明成员函数有一个隐式的this参数绑定时需要传递对象指针a。5.5 bind 的作用统一成无参函数void output(int a, int b, int c) { cout a , b , c endl; } int main() { // 把三个参数都固定变成无参函数 auto f std::bind(output, 1, 2, 3); f(); // 输出1, 2, 3 return 0; }核心优势可以把任何带参数的函数转换成无参函数方便存储到容器或作为回调函数。六、完整示例下面的代码综合演示了std::function和std::bind的用法。#include iostream #include functional using namespace std; using namespace std::placeholders; // 1. 普通函数 int add(int a, int b) { return a b; } // 2. 仿函数 struct Multiply { int operator()(int a, int b) const { return a * b; } }; // 3. 成员函数 class Calculator { public: int divide(int a, int b) const { return a / b; } }; int main() { // std::function 统一存储不同类型的可调用对象 std::functionint(int, int) func; // 存储函数指针 func add; cout add: func(10, 20) endl; // 30 // 存储仿函数 Multiply mul; func mul; cout multiply: func(10, 20) endl; // 200 // 存储 Lambda func [](int a, int b) { return a - b; }; cout lambda: func(10, 20) endl; // -10 // std::bind 绑定成员函数 Calculator calc; func std::bind(Calculator::divide, calc, _1, _2); cout divide: func(20, 10) endl; // 2 // bind 固定部分参数 auto fixed std::bind(add, 100, _1); cout fixed: fixed(50) endl; // 150 return 0; }示例说明std::functionint(int, int)定义了一个统一类型接收两个int返回int函数指针、仿函数、Lambda 都可以赋值给funcstd::bind可以将成员函数或普通函数绑定成新的可调用对象七、总结知识点核心要点函数指针指向函数的指针int (*pf)(int, int)仿函数重载operator()的类对象add(10,20)实际是add.operator()(10,20)仿函数特点可保存状态、可重载、可作算法参数std::function通用可调用对象包装器统一存储函数指针、仿函数、Lambdastd::bind绑定函数参数生成新可调用对象占位符_1、_2、_3表示调用时传入的参数位置绑定成员函数需要传递对象指针obj因为有隐式thisbind 作用把带参函数转成无参函数方便存储到容器或作为回调一句话总结std::function统一存储各类可调用对象std::bind绑定参数生成新函数配合占位符可重排参数顺序。仿函数通过operator()让对象像函数一样调用。

更多文章