如何创建键为string、值为类成员函数指针的std::map
类成员函数的std::map实现方案
问题描述
原本使用全局函数时,可以直接将函数指针存入std::map实现命令映射:
void nvmRead(unsigned char* msg, unsigned char* resp_frame); void nvmWrite(unsigned char* msg, unsigned char* resp_frame); void nvmProvision(unsigned char* msg, unsigned char* resp_frame); void nvmStatus(unsigned char* msg, unsigned char* resp_frame); std::map<std::string, void (*)(unsigned char* msg, unsigned char* resp_frame)> command_table = { { "NVM_READ", nvmRead }, { "NVM_WRITE", nvmWrite }, { "NVM_PROVISION", nvmProvision }, { "NVM_STATUS", nvmStatus } };
调用时直接通过command_table.at(s)(parm1, parm2);即可。但将这些函数移到类内部后,出现类型转换错误:
error: could not convert '{{"NVM_READ", ((myns::RaidDevCommInTask*)this)->myns::RaidDevCommInTask::nvmRead}, {"NVM_WRITE", ((myns::RaidDevCommInTask*)this)->myns::RaidDevCommInTask::nvmWrite}, {"NVM_PROVISION", ((myns::RaidDevCommInTask*)this)->myns::RaidDevCommInTask::nvmProvision}, {"NVM_STATUS", ((myna::RaidDevCommInTask*)this)->myns::RaidDevCommInTask::nvmStatus}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, void (*)(unsigned char*, unsigned char*)>' 85 | };
构造函数中赋值同样报错:
src/tests/raid_dev_comm_in_task.cpp:40:31: error: cannot convert 'myns::RaidDevCommInTask::nvmRead' from type 'void (myns::RaidDevCommInTask::)(unsigned char*, unsigned char*)' to type 'std::map<std::__cxx11::basic_string<char>, void (*)(unsigned char*, unsigned char*)>::mapped_type' {aka 'void (*)(unsigned char*, unsigned char*)'} 40 | command_table["NVM_READ"] = nvmRead; | ^~~~~~~
核心原因
类成员函数指针与普通全局函数指针类型不兼容:成员函数隐含了一个this指针作为第一个参数,因此它的类型是void (类名::*)(参数列表),无法直接转换为普通函数指针void (*)(参数列表)。
解决方案
方案1:使用成员函数指针作为map值类型
直接定义成员函数指针类型的map,调用时需要通过对象实例(如this)来调用成员函数指针。
#include <map> #include <string> namespace myns { class RaidDevCommInTask { private: void nvmRead(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑,可访问类私有成员 } void nvmWrite(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } void nvmProvision(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } void nvmStatus(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } // 定义成员函数指针的别名,简化类型书写 using CommandFunc = void (RaidDevCommInTask::*)(unsigned char*, unsigned char*); std::map<std::string, CommandFunc> command_table = { {"NVM_READ", &RaidDevCommInTask::nvmRead}, {"NVM_WRITE", &RaidDevCommInTask::nvmWrite}, {"NVM_PROVISION", &RaidDevCommInTask::nvmProvision}, {"NVM_STATUS", &RaidDevCommInTask::nvmStatus} }; public: void executeCommand(const std::string& cmd, unsigned char* msg, unsigned char* resp) { auto iter = command_table.find(cmd); if (iter != command_table.end()) { // 必须通过对象实例调用成员函数指针,语法为:(对象->*成员指针)(参数) (this->*(iter->second))(msg, resp); } } }; } // namespace myns
方案2:使用std::function + std::bind绑定this
将成员函数与当前对象的this绑定,封装成std::function类型,调用方式更接近原全局函数的用法。
#include <map> #include <string> #include <functional> namespace myns { class RaidDevCommInTask { private: void nvmRead(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } void nvmWrite(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } void nvmProvision(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } void nvmStatus(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } std::map<std::string, std::function<void(unsigned char*, unsigned char*)>> command_table; public: RaidDevCommInTask() { // 用std::bind绑定this和成员函数,占位符表示后续传入的参数 command_table["NVM_READ"] = std::bind(&RaidDevCommInTask::nvmRead, this, std::placeholders::_1, std::placeholders::_2); command_table["NVM_WRITE"] = std::bind(&RaidDevCommInTask::nvmWrite, this, std::placeholders::_1, std::placeholders::_2); command_table["NVM_PROVISION"] = std::bind(&RaidDevCommInTask::nvmProvision, this, std::placeholders::_1, std::placeholders::_2); command_table["NVM_STATUS"] = std::bind(&RaidDevCommInTask::nvmStatus, this, std::placeholders::_1, std::placeholders::_2); } void executeCommand(const std::string& cmd, unsigned char* msg, unsigned char* resp) { auto iter = command_table.find(cmd); if (iter != command_table.end()) { // 直接调用,无需手动处理this iter->second(msg, resp); } } }; } // namespace myns
方案3:使用Lambda捕获this(C++11及以上)
用Lambda表达式捕获当前对象的this,直接在Lambda内部调用成员函数,写法更简洁直观。
#include <map> #include <string> #include <functional> namespace myns { class RaidDevCommInTask { private: void nvmRead(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } void nvmWrite(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } void nvmProvision(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } void nvmStatus(unsigned char* msg, unsigned char* resp_frame) { // 实现逻辑 } std::map<std::string, std::function<void(unsigned char*, unsigned char*)>> command_table; public: RaidDevCommInTask() { // Lambda捕获this,直接调用成员函数 command_table["NVM_READ"] = [this](unsigned char* msg, unsigned char* resp) { this->nvmRead(msg, resp); }; command_table["NVM_WRITE"] = [this](unsigned char* msg, unsigned char* resp) { this->nvmWrite(msg, resp); }; command_table["NVM_PROVISION"] = [this](unsigned char* msg, unsigned char* resp) { this->nvmProvision(msg, resp); }; command_table["NVM_STATUS"] = [this](unsigned char* msg, unsigned char* resp) { this->nvmStatus(msg, resp); }; } void executeCommand(const std::string& cmd, unsigned char* msg, unsigned char* resp) { auto iter = command_table.find(cmd); if (iter != command_table.end()) { iter->second(msg, resp); } } }; } // namespace myns
内容的提问来源于stack exchange,提问作者chriskot
相关产品推荐
相关产品推荐

