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_fun
和std::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组件交互时。
函数适配器与相关技术的关系可以用以下公式表示: