C++ STL模板库4-函数适配器
文章目录
- C++ STL模板库4-函数适配器
-
- 一、绑定器(Binder)
- 二、取反器和普通函数成员函数适配器
- 三、推荐替换方案
C++的函数适配器主要用于扩展或特化
函数对象(仿函数)的功能,使其适配算法或特定需求。
一、绑定器(Binder)
将二元函数对象的一个参数绑定到固定值,转换为一元函数对象(就是反函数):
bind1st(已弃用)
- 绑定二元函数对象的第一个参数到指定值。
- 示例:bind1st(greater<int>(), 5) 将比较值固定为5,检查元素是否大于5 。
- 需要从unary_function<int, int, void>继承
- 只能绑定仿函数
- 在C++11后已弃用
bind2nd(已弃用)
- 绑定二元函数对象的第二个参数到指定值。
- 示例:bind2nd(less<int>(), 10) 将比较值固定为10,检查元素是否小于10 。
- 需要从binary_function<int, int, void>继承
- 只能绑定仿函数
- 在C++11后已弃用
示例:
#include <iostream>
#include <vector>
#include <algorithm>//包含for_each/find_if算法头的文件
#include <functional>//系统定义的仿函数和适配器头文件
struct Compare:public std::binary_function<int, int, bool> // c++ 以上已经移除
{
bool operator()(int a, int b)const
{
return a>b;
}
};
int main() {
std::vector<int> vec = {3, 6, 1, 8, 4};
// 查找第一个小于5的元素(结果指向3) 绑定参数到仿函数的第一个参数
auto it = std::find_if(vec.begin(), vec.end(), std::bind1st(Compare(), 5));
if (it != vec.end()) {
std::cout << "找到第一个小于 5 的元素: " << *it << std::endl;
}
// 查找第一个大于5的元素(结果指向6) 绑定参数到仿函数的第二个参数
it = std::find_if(vec.begin(), vec.end(), std::bind2nd(Compare(), 5));
if (it != vec.end()) {
std::cout << "找到第一个大于 5 的元素: " << *it << std::endl;
}
return 0;
}
bind
- 绑定参数到函数的任意位置。
- 可绑定成员函数、普通函数及Lambda表达式
[示例]
#include <iostream>
#include <vector>
#include <algorithm>//包含for_each/find_if算法头的文件
#include <functional>//系统定义的仿函数和适配器头文件
bool Compare(int a, int b)
{
return a>b;
}
void show(int a,char c, float f)
{
std::cout<<a<<" "<<c<<" "<<f<<std::endl;
}
int main() {
std::vector<int> vec = {3, 6, 1, 8, 4};
// 查找第一个小于5的元素(结果指向3) 绑定参数到仿函数的第一个参数
auto it = std::find_if(vec.begin(), vec.end(), std::bind(Compare,5,std::placeholders::_1));
if (it != vec.end()) {
std::cout << "找到第一个小于 5 的元素: " << *it << std::endl;
}
// 查找第一个大于5的元素(结果指向6) 绑定参数到仿函数的第二个参数
it = std::find_if(vec.begin(), vec.end(), std::bind(Compare, std::placeholders::_1, 5));
if (it != vec.end()) {
std::cout << "找到第一个大于 5 的元素: " << *it << std::endl;
}
show(2, 'A',3.14f);
auto fun = std::bind(show,3,'C',2.15f);
fun();//输出是 3 C 2.15
fun(4, 'H',2.14);//输出还是 3 C 2.15 ,始终是绑定的参数
show(2, 'A',3.14f);//2 A 3.14 绑定不会改变原来的实际值。
return 0;
}
-
如果某个位置的参数不要绑定可以用std::placeholders::_N占位符,placeholders是一个命名空间,里面定义了_1到_20个占位符对象
-
占位符占的是传参时可以改变的,_1是第一个参数_2是第二个参数,_3是第三个参数……
-
占位符的顺序要和函数的顺序一致,结果才能保证正确
-
参数类型一致性,std::placeholders::_3表示传参时第三个参数所放的位置
//第三个参数放到二个位置
std::bind(show,std::placeholders::_1,std::placeholders::_3,9);//第三个参数放到第一个位置
std::bind(show,std::placeholders::_3,std::placeholders::_1,9); -
std::bind生成的函数会忽略多余参数,但需确保必要参数已绑定。
#include <iostream>
#include <vector>
#include <algorithm>//包含for_each/find_if算法头的文件
#include <functional>//系统定义的仿函数和适配器头文件void show(int n1,int n2, int n3)
{
std::cout<<n1<<" "<<n2<<" "<<n3<<std::endl;
}
int main() {show(1, 2,3);//1 2 3
//参数第一个用占位符占了,是可以改变的,第二个和第三个被绑定是固定值,传任何参数都不改变
auto fun = std::bind(show,std::placeholders::_1,4,5);
fun(30);//输出是 30 4 5
fun(30,'G',8);//输出是 30 4 5std::cout<<"\\n";
auto fun1 = std::bind(show,std::placeholders::_1,std::placeholders::_2,9);
fun1(4,5);//4 5 9 –show(4, 5, 9)
fun1(1,2,3,4,6,7,8,"…..","你好"); //1 2 9 –show(1, 2, 9)
// fun1(4);// 错误至少两个参数,占位的必须传参std::cout<<"\\n"; //调换位置
auto fun2 = std::bind(show,std::placeholders::_2,std::placeholders::_1,9);
fun2(4,5);//5 4 9 –show(5, 4, 9)
fun2(5,4);//4 5 9 –show(4, 5, 9)std::cout<<"\\n";
auto fun3 = std::bind(show,std::placeholders::_1,9,std::placeholders::_2);
fun3(4,5);//4 9 5 –show(4, 5, 9)std::cout<<"\\n";
auto fun4 = std::bind(show,std::placeholders::_1,9,std::placeholders::_3);
// fun4(4,5);//错误 ,少一个参数 ,最少三个参数
fun4(4,5,9);//4 9 9 –show(4, 9, 9)std::cout<<"\\n";
auto fun5= std::bind(show,std::placeholders::_1,std::placeholders::_3,9);
fun5(4,5,7);//4 7 9 –show(4, 7, 9)
fun5(4,5,7,1,2,3);//4 7 9 –show(4, 7, 9)
// fun5(4,5);// 错误 ,少一个参数 ,最少三个参数std::cout<<"\\n";
auto fun6= std::bind(show,std::placeholders::_2,std::placeholders::_2,9);
fun6(4,5,7);//5 5 9 –show(5, 5, 9)return 0;
} -
函数重载需显式指定函数类型 函数重载,std::bind无法自动推断重载版本,导致编译器解析失败 解决方案:显式指定函数类型,例如使用static_cast或模板参数。 注意函数名要加括号
#include <iostream>
#include <functional>//系统定义的仿函数和适配器头文件void show(int a,char c, float f)
{
std::cout<<a<<" "<<c<<" "<<f<<std::endl;
}void show(int n1,int n2, int n3)
{
std::cout<<n1<<" "<<n2<<" "<<n3<<std::endl;
}
int main() {show(2, 'A',3.14f);//2 A 3.14
// 显式指定绑定函数 show(int a,char c, float f)
auto fun = std::bind(static_cast<void(*)(int, char, float)>(show),std::placeholders::_1,'C',2.15f);
fun(30);//输出是 30 C 2.15// 显式指定绑定函数 show(int n1,int n2, int n3)
std::cout<<"\\n";
auto fun1 = std::bind(static_cast<void(*)(int, char, float)>(show),std::placeholders::_1,'b',std::placeholders::_2);
fun1(15,1);//15 b 1
fun1(15,1,3);//15 b 1return 0;
}
std::bind 仍是合法且有效的工具,但导致复杂逻辑易导致代码冗长。建议改用更简洁、直观,且类型安全的Lambda 表达式.
二、取反器和普通函数成员函数适配器
谓词(predicate ) 返回值是bool类型的仿函数。一个参数的是一元谓词,两个参数的是二元谓词。
翻转函数对象的返回值逻辑:
not1
- 取反一元谓词的返回值。
- 示例:not1(bind2nd(greater<int>(), 5)) 筛选不大于5的元素。
not2
- 取反二元谓词的返回值。
- 示例:not2(equal_to<int>()) 判断两个元素是否不相等。
普通函数适配器ptr_fun
- 将普通函数转换为可被STL算法调用的函数对象。
- 示例:bind2nd(ptr_fun(myPrintInt01), 1000) 绑定普通函数的第二个参数为1000 。
not1,not2,ptr_fun都是现代C++中已弃用的功能,建议改用更简洁、直观,且类型安全的Lambda 表达式.
auto isOdd = [](int n) { return !isEven(n); }; // 直接取反
成员函数适配器 用于将类成员函数转换为可被STL算法调用的函数对象:
- 适配指向对象指针的成员函数。
- 示例:for_each(v.begin(), v.end(), mem_fun(&Person::show)) 调用对象指针的成员函数。
- 适配引用类型的成员函数。
- 示例:for_each(v.begin(), v.end(), mem_fun_ref(&Person::show)) 调用对象引用的成员函数。
- 此两者都已弃用建议改用Lambda 表达式.
三、推荐替换方案
Lambda 表达式 auto predicate = [](int a) { return compare(a, 2); }; // 直接内联逻辑
std::function 结合 Lambda
std::function<bool(int)> predicate =
[ ](int a) { return compare(a, 2); }; // 显式类型声明
-
必须显式声明 std::function 的参数和返回类型,否则会导致编译错误。 示例:std::function<void(int)> func = print;(print 函数需接受 int 参数)。
-
优化建议:在性能敏感场景中,优先使用模板或直接传递 Lambda。
-
std::function 可以被赋值为 nullptr,调用空函数对象会触发 std::bad_function_call 异常。 示例:
std::function<void()> func = nullptr;
if (func) func(); // 需要显式检查 -
不支持存储 const 或 volatile 成员函数(需通过 std::bind 适配)。
-
避免过度使用- 对于已知类型的函数,直接使用模板或函数指针更高效。
用法示例
转换普通函数
#include <functional>
#include <iostream>
void print(int n) { std::cout << n << " "; }
int main() {
std::function<void(int)> func = print; // 直接赋值普通函数
func(5); // 输出:5
return 0;
}
存储 Lambda 表达式
#include <functional>
#include <iostream>
int main() {
std::function<int(int, int)> add = [](int a, int b) { return a + b; };
std::cout << add(3, 4) << std::endl; // 输出:7
return 0;
}
存储成员函数
#include <functional>
#include <iostream>
class Person {
public:
void show() const { std::cout << "Hello" << std::endl; }
};
int main() {
Person p;
std::function<void(Person*)> func = &Person::show;
func(&p); // 输出:Hello
return 0;
}
存储其他函数对象
#include <functional>
#include <iostream>
struct Adder {
int operator()(int a, int b) { return a + b; }
};
int main() {
std::function<int(int, int)> func = Adder{};
std::cout << func(2, 3) << std::endl; // 输出:5
return 0;
}
评论前必须登录!
注册