如何为C++成员函数生成唯一键以用于unordered_map?
解决C++20 Mock类无需手动传函数名生成唯一成员函数键的方案
核心思路
利用C++17起支持的**auto模板参数**,将成员函数指针直接作为模板参数传入,通过编译期生成的唯一静态变量地址作为unordered_map的键,彻底避免手动传入函数名字符串。
完整实现代码
#include <unordered_map> #include <any> #include <type_traits> #include <cassert> template<typename T> class Mock { private: // 用静态变量的唯一地址作为键,存储对应返回值 std::unordered_map<const void*, std::any> m_return_values; // 针对每个成员函数指针,生成唯一静态变量,返回其地址作为键 template<auto MemFunc> static const void* get_unique_key() { static const char dummy; // 每个MemFunc对应唯一的dummy实例 return &dummy; } public: // 设置成员函数的返回值:自动推导返回值类型,无需传函数名 template<auto MemFunc, typename Ret = std::invoke_result_t<decltype(MemFunc), T&>> void set_return(Ret&& ret) { const void* key = get_unique_key<MemFunc>(); m_return_values[key] = std::forward<Ret>(ret); } // 获取成员函数的返回值:直接通过成员函数指针定位 template<auto MemFunc, typename Ret = std::invoke_result_t<decltype(MemFunc), T&>> Ret get_return() { const void* key = get_unique_key<MemFunc>(); return std::any_cast<Ret>(m_return_values.at(key)); } }; // 测试用例 class Foo { public: virtual int f() = 0; virtual int g() = 0; virtual std::string hello() = 0; }; int main() { Mock<Foo> mock; // 期望的简化调用方式:直接传成员函数指针,无需函数名 mock.set_return<&Foo::f>(42); mock.set_return<&Foo::g>(69); mock.set_return<&Foo::hello>("Hello Mock!"); assert(mock.get_return<&Foo::f>() == 42); assert(mock.get_return<&Foo::g>() == 69); assert(mock.get_return<&Foo::hello>() == "Hello Mock!"); }
关键细节说明
编译期唯一键生成:
对于每个不同的成员函数指针MemFunc,模板函数get_unique_key<MemFunc>会生成一个独立的静态变量dummy,其内存地址在整个程序生命周期中是唯一的,完美解决同签名成员函数的区分问题。自动类型推导:
利用std::invoke_result_t自动推导成员函数的返回值类型,无需手动指定,进一步简化调用逻辑。重载成员函数处理:
如果存在重载的成员函数,只需显式指定成员函数的类型即可,例如:class Foo { public: int f(); int f(int); }; // 区分重载的f()和f(int) mock.set_return<static_cast<int (Foo::*)()>(&Foo::f)>(100); mock.set_return<static_cast<int (Foo::*)(int)>(&Foo::f)>(200);标准合规性:
该方案完全符合C++标准,没有使用任何未定义行为,跨平台兼容性良好。
内容的提问来源于stack exchange,提问作者MockingMocker
相关产品推荐
相关产品推荐

