如何提取C++成员函数指针去除类限定后的普通函数类型
问题结论
不存在符合C++标准的通用方案,可以直接抹除成员函数指针的类作用域限定,得到你举例的那种签名完全对应的普通函数指针,也就是void(Foo::*)()到void(*)()这种直接类型等价转换是做不到的。
核心原因
- 非静态成员函数和普通函数的调用规则本质不同:非静态成员函数调用时会隐式追加
this指针作为第一个隐含参数,签名里没写不代表这个参数不存在,二者的调用约定、栈帧结构完全不匹配。 - 成员函数指针本身的内存布局不固定:受类的继承关系(比如是否有虚继承、多继承)、成员函数的限定符影响,成员函数指针的长度在不同场景下可能从8字节到24字节不等,根本无法和固定长度的普通函数指针做等价转换。
- 任何用
reinterpret_cast强行把成员函数指针转成普通函数指针的写法,都是标准明确规定的未定义行为,没有可移植性,调用时大概率会出现内存访问错误、逻辑跑飞的问题。
你的实际需求可以实现
你想要从仿函数的operator()类型自动推导签名、直接给std::function传参的目标,完全不需要做“成员函数指针转普通函数指针”这种危险操作,只需要做类型提取即可:通过模板特化把成员函数指针里的返回值类型、参数列表拆出来,重新拼接成std::function需要的函数签名。
实现代码
#include <functional> // 主模板,默认匹配普通可调用类型 template <class T> struct magic_get_function_type { using type = T; }; // 特化:匹配无CV限定、无引用限定的非静态成员函数指针 template <class Ret, class ClassType, class... Args> struct magic_get_function_type<Ret (ClassType::*)(Args...)> { using type = Ret(Args...); }; // 特化:匹配const修饰的成员函数(绝大多数仿函数的operator()都是const属性) template <class Ret, class ClassType, class... Args> struct magic_get_function_type<Ret (ClassType::*)(Args...) const> { using type = Ret(Args...); }; // 按需扩展其他限定符的特化:volatile、const volatile、左值引用&、右值引用&&即可 template <class Ret, class ClassType, class... Args> struct magic_get_function_type<Ret (ClassType::*)(Args...) volatile> { using type = Ret(Args...); }; template <class Ret, class ClassType, class... Args> struct magic_get_function_type<Ret (ClassType::*)(Args...) const volatile> { using type = Ret(Args...); };
使用示例
struct Functor { void operator()(int x, double y, float z) const { // 自定义逻辑 } }; int main() { Functor f; // 自动推导得到签名void(int, double, float),std::function可直接接收仿函数实例 std::function<magic_get_function_type<decltype(&Functor::operator())>::type> stdfunc{f}; stdfunc(1, 2.3, 4.5f); // 正常调用 return 0; }
注意:写
decltype的时候必须对Functor::operator()取地址,也就是加&,否则拿到的是成员函数的左值引用类型,匹配不到我们写的指针特化版本。
补充说明
这个实现全程没有做任何危险的类型强制转换,只是在编译期完成类型的拆分和重组,完全符合C标准,在所有支持C11及以上版本的编译器上都可以正常工作。std::function本身就具备存储、调用仿函数的能力,你只需要给它传递正确的签名即可,不需要把成员函数本身转成普通函数指针。
内容的提问来源于stack exchange,提问作者JensB
相关产品推荐
相关产品推荐

