[原创](Modern C++)现代C++的关键性概念: std::mem_fn是std::bind的轻量级版本, 它们的区别是什么?
[作者]
常用网名: 猪头三
出生日期: 1981.XX.XX
企鹅交流: 643439947
个人网站: 80x86汇编小站
编程生涯: 2001年~至今[共24年]
职业生涯: 22年
开发语言: C/C++、80x86ASM、Object Pascal、Objective-C、C#、R、Python、PHP、Perl、
开发工具: Visual Studio、Delphi、XCode、C++ Builder、Eclipse
技能种类: 逆向 驱动 磁盘 文件 大数据分析
涉及领域: Windows应用软件安全/Windows系统内核安全/Windows系统磁盘数据安全/macOS应用软件安全
项目经历: 股票模型量化/磁盘性能优化/文件系统数据恢复/文件信息采集/敏感文件监测跟踪/网络安全检测
专注研究: 机器学习、股票模型量化、金融分析
[序言]
在现代C++编程中, 函数对象和回调机制被广泛应用, 尤其是在泛型编程和并发编程中, 将成员函数和成员变量进行绑定变得十分常见. 传统上, 可以使用std::bind来将类成员函数绑定为可调用对象, 但它在功能强大的同时, 也会引入一定的复杂性和额外开销. 相对而言, std::mem_fn提供了一种更轻量级的解决方案, 专注于将成员函数或成员变量转换为可调用对象, 从而在简单场景下获得更清晰和高效的实现.
[代码演示]
#include <iostream>
#include <memory>
#include <functional>
#include <fcntl.h>
#include <io.h>
#include <cwchar>
struct MEM_FN
{
// 无参成员函数, 返回一个宽字符字符串
std::wstring mpu_display_Str()
{
return L"Hello World";
}
// 带有1个参数的成员函数, 返回输入参数加1的结果
int mpu_display_Num(int int_param_1)
{
return int_param_1 + 1;
}
// 带有2个参数的成员函数, 返回成员变量与参数之和
int mpu_display_Add(int int_param_1, int int_param_2)
{
return mpu_Data + int_param_1 + int_param_2;
}
// 模板成员函数, 使用折叠表达式求和
template<typename... Args>
int mpu_display_Add_many(Args... args)
{
return (args + ...);
}
int mpu_Data{ 7 };
};
int main() {
// 设置标准输出为宽字符模式
_setmode(_fileno(stdout), _O_WTEXT);
auto class_Mem_Fn = MEM_FN{};
// 绑定无参数的成员函数, 通过 std::mem_fn 生成可调用对象
auto fn_display_Str = std::mem_fn(&MEM_FN::mpu_display_Str);
std::wcout << fn_display_Str(class_Mem_Fn) << std::endl;
// 绑定带有1个参数的成员函数, 调用时传入类对象和参数
auto fn_display_Num = std::mem_fn(&MEM_FN::mpu_display_Num);
std::wcout << fn_display_Num(class_Mem_Fn, 1) << std::endl;
// 绑定带有2个参数的成员函数, 调用时依次传入类对象和参数
auto fn_display_Add = std::mem_fn(&MEM_FN::mpu_display_Add);
std::wcout << fn_display_Add(class_Mem_Fn, 1, 2) << std::endl;
// 绑定模板成员函数, 注意支持类对象指针传入
auto pclass_Mem_Fn = std::make_unique<MEM_FN>();
auto fun_display_Add_many = std::mem_fn(&MEM_FN::mpu_display_Add_many<short, int, long>);
std::wcout << fun_display_Add_many(pclass_Mem_Fn, 1, 2, 3) << std::endl;
// 绑定类成员变量, 可直接通过调用获取变量值
auto fn_access_Data = std::mem_fn(&MEM_FN::mpu_Data);
std::wcout << fn_access_Data(pclass_Mem_Fn) << std::endl;
std::cin.get();
return 0;
}
[代码说明]
1. std::mem_fn绑定成员函数
通过std::mem_fn可以将成员函数指针转换为可调用对象, 该对象在调用时会自动传入类对象或类对象指针作为第一个参数. 例如, 对于无参函数, 调用时只需传入类对象即可, 对于带参函数, 调用时需要依次传入类对象和相应参数. 与std::bind相比, std::mem_fn不支持占位符等高级绑定功能, 但在简单绑定场景下更为直观和高效.
2. 支持模板成员函数和成员变量的绑定
利用std::mem_fn绑定模板成员函数mpu_display_Add_many(), 此时需要显式指定模板参数, 同时也利用std::mem_fn直接绑定成员变量mpu_Data. 这种绑定方式既保证了代码的简洁性, 又能提高代码的可读性和性能, 因为不涉及额外的参数转换或包装.
3. 与std::bind的对比
std::bind提供了更为灵活的参数占位符和参数重排序功能, 适用于复杂绑定需求; 而std::mem_fn则专注于简单, 直接地将成员函数或成员变量转换为可调用对象. 因此, 当仅需简单绑定时, 推荐使用 std::mem_fn以减少额外开销和代码复杂度.
[总结]
std::mem_fn是C++11引入的一个轻量级工具, 专注于将类的成员函数或成员变量转换为可调用的函数对象. 与功能更强大的std::bind相比, 两者在以下几个方面存在显著区别:
1. 设计目标与轻量级特性
std::mem_fn专注于绑定类的成员函数和成员变量, 实现简单且高效.
std::bind是一个通用工具, 不仅能绑定成员函数, 还能处理普通函数、参数占位符和参数重排, 功能更丰富但开销稍大.
2. 使用场景
如果仅需将成员函数或变量封装为函数对象, std::mem_fn是更直接的选择.
若需绑定特定参数值或调整参数顺序, std::bind则更适合.
3. 性能与开销
std::mem_fn因其单一职责, 在简单场景下通常比std::bind更高效.
std::bind由于支持复杂功能, 在运行时可能引入额外开销.
4. 代码可读性
std::mem_fn意图明确, 代码简洁, 适合表达简单的绑定需求.
std::bind在复杂场景下可能需要更多代码, 可读性稍逊.
在实际开发中, 选择std::mem_fn还是std::bind取决于具体需求. 对于简单的成员函数调用或成员变量访问, std::mem_fn提供了更高的效率和清晰度; 而对于需要参数绑定或更灵活操作的场景, std::bind则是不二之选. 通过理解两者的特点, 可以在现代C++中编写更优雅、更高效的代码.