跳转到内容

C++ 函数适配器

来自代码酷

C++函数适配器[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

函数适配器(Function Adapters)是C++标准模板库(STL)中用于修改或扩展函数对象行为的工具。它们通过包装现有的函数对象、函数指针或成员函数,改变其调用方式或参数数量,从而增强代码的灵活性和复用性。

在STL中,函数适配器主要分为三类:

  • 绑定器(Binders):固定函数的部分参数
  • 否定器(Negators):反转谓词的逻辑
  • 成员函数适配器(Member Function Adapters):将成员函数包装为函数对象

绑定器(std::bind)[编辑 | 编辑源代码]

C++11引入的std::bind是最常用的通用绑定器,可以绑定参数、重排参数顺序或设置占位符。

基本语法[编辑 | 编辑源代码]

#include <functional>
auto new_callable = std::bind(callable, arg_list);

示例1:绑定固定参数[编辑 | 编辑源代码]

#include <iostream>
#include <functional>

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

int main() {
    auto print_plus_10 = std::bind(print_sum, std::placeholders::_1, 10);
    print_plus_10(5);  // 输出15 (5+10)
}

示例2:重排参数顺序[编辑 | 编辑源代码]

#include <iostream>
#include <functional>

void print_division(float a, float b) {
    std::cout << a / b << '\n';
}

int main() {
    // 将第二个参数绑定到第一个位置,第一个参数绑定到第二个位置
    auto invert_division = std::bind(print_division, std::placeholders::_2, std::placeholders::_1);
    invert_division(10, 2);  // 输出0.2 (2/10)
}

否定器(std::not_fn)[编辑 | 编辑源代码]

C++17引入的std::not_fn用于反转谓词的逻辑结果。

示例[编辑 | 编辑源代码]

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>

bool is_even(int n) { return n % 2 == 0; }

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    
    // 计算奇数数量
    int count = std::count_if(v.begin(), v.end(), std::not_fn(is_even));
    std::cout << count;  // 输出3
}

成员函数适配器[编辑 | 编辑源代码]

STL提供了适配器来调用成员函数,包括:

  • std::mem_fn:将成员函数包装为函数对象
  • 过时的std::mem_funstd::mem_fun_ref

现代用法(C++11及以上)[编辑 | 编辑源代码]

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>

struct Person {
    std::string name;
    int age;
    void print() const { std::cout << name << " (" << age << ")\n"; }
};

int main() {
    std::vector<Person> people = {{"Alice", 25}, {"Bob", 30}};
    
    // 使用mem_fn调用成员函数
    std::for_each(people.begin(), people.end(), std::mem_fn(&Person::print));
}

实际应用场景[编辑 | 编辑源代码]

场景1:回调函数定制[编辑 | 编辑源代码]

在事件处理系统中,可以使用绑定器为不同事件创建特定的回调:

#include <functional>
#include <iostream>

class Button {
public:
    using Callback = std::function<void()>;
    
    void setCallback(Callback cb) { callback = cb; }
    void click() { if(callback) callback(); }
    
private:
    Callback callback;
};

void log_message(const std::string& msg, int severity) {
    std::cout << "[" << severity << "] " << msg << "\n";
}

int main() {
    Button btn;
    // 绑定特定日志消息和严重级别
    btn.setCallback(std::bind(log_message, "Button clicked!", 1));
    btn.click();  // 输出: [1] Button clicked!
}

场景2:算法参数定制[编辑 | 编辑源代码]

在STL算法中使用适配器定制比较行为:

#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>

int main() {
    std::vector<int> v = {5, 3, 1, 4, 2};
    
    // 使用绑定器创建自定义比较器
    auto greater_than = [](int x, int y) { return x > y; };
    std::sort(v.begin(), v.end(), std::bind(greater_than, std::placeholders::_2, std::placeholders::_1));
    
    for(int n : v) std::cout << n << ' ';  // 输出: 1 2 3 4 5
}

性能考虑[编辑 | 编辑源代码]

函数适配器会引入一定的开销:

  • 额外的间接调用
  • 可能的堆分配(对于捕获大量数据的绑定表达式)
  • 内联机会减少

在性能关键路径中,应考虑直接使用lambda表达式或手写函数对象。

现代C++替代方案[编辑 | 编辑源代码]

C++11及以后版本中,lambda表达式通常可以替代函数适配器,提供更清晰的语法:

绑定器替代[编辑 | 编辑源代码]

// 使用std::bind
auto plus_10 = std::bind(std::plus<int>(), std::placeholders::_1, 10);

// 使用lambda
auto plus_10_lambda = [](int x) { return x + 10; };

成员函数适配器替代[编辑 | 编辑源代码]

// 使用std::mem_fn
std::for_each(people.begin(), people.end(), std::mem_fn(&Person::print));

// 使用lambda
std::for_each(people.begin(), people.end(), [](const Person& p) { p.print(); });

总结[编辑 | 编辑源代码]

函数适配器是STL中强大的工具,允许开发者:

  • 修改现有函数对象的调用方式
  • 减少需要编写的样板代码
  • 创建更灵活的组件

随着C++的发展,lambda表达式在许多场景下提供了更直观的替代方案,但理解函数适配器仍然很重要,特别是在维护遗留代码或需要与现有STL组件交互时。

graph TD A[函数适配器] --> B[绑定器] A --> C[否定器] A --> D[成员函数适配器] B --> E[std::bind] B --> F[过时的bind1st/bind2nd] C --> G[std::not_fn] C --> H[过时的not1/not2] D --> I[std::mem_fn] D --> J[过时的mem_fun/mem_fun_ref]

函数适配器与相关技术的关系可以用以下公式表示: 适配后的函数=适配器原始函数