成员函数指针类型转换是否合法?EventHandler实现通用性问询
我编写了一个用于处理和执行函数指针的泛型类EventHandler,它是std::function和std::bind的简化等效实现。为了处理成员函数,我将其转换为内部EventHandler::Class类型。请问这种转换方式是否合法?在调用被处理函数的所有场景下都能正常工作吗?
实现代码
template <typename ReturnType, typename... Arguments> class EventHandler { class Class {}; ReturnType (Class::*memberFunction)(Arguments...) = nullptr; union { Class *owner; ReturnType(*function)(Arguments...) = nullptr; }; public: EventHandler() = default; EventHandler(EventHandler &&) = default; EventHandler(const EventHandler &) = default; EventHandler &operator=(EventHandler &&) = default; EventHandler &operator=(const EventHandler &) = default; EventHandler(ReturnType (*function)(Arguments...)) : function(function) { } template <typename Owner> EventHandler(Owner *owner, ReturnType (Owner::*memberFunction)(Arguments...)) : memberFunction((ReturnType (Class::*)(Arguments...)) memberFunction), owner((Class *) owner) { } template <typename Owner> EventHandler(const Owner *owner, ReturnType (Owner::*memberFunction)(Arguments...) const) : memberFunction((ReturnType (Class::*)(Arguments...)) memberFunction), owner((Class *) owner) { } ReturnType operator()(Arguments... arguments) { return memberFunction ? (owner ? (owner->*memberFunction)(arguments...) : ReturnType()) : (function ? function(arguments...) : ReturnType()); } };
该实现支持全局函数、成员函数和const成员函数的处理,为简洁起见未展示volatile和const volatile相关版本。
支持的函数类型示例
class Object { public: double y = 1000; Object() = default; Object(double y) : y(y) {} static void s1(void) { std::cout << "s1()" << std::endl; } static void s2(int a) { std::cout << "s2(a:" << 10 + a << ")" << std::endl; } static void s3(int a, float b) { std::cout << "s3(a:" << 10 + a << ", b:" << 10 + b << ")" << std::endl; } static int s4(void) { std::cout << "s4(): "; return 10 + 4; } static Object s5(int a) { std::cout << "s5(a:" << 10 + a << "): "; return Object(10 + 5.1); } static float s6(int a, Object b) { std::cout << "s6(a:" << 10 + a << ", b:" << 10 + b.y << "); "; return 10 + 6.2f; } void m1(void) { std::cout << "m1()" << std::endl; } void m2(int a) { std::cout << "m2(a:" << y + a << ")" << std::endl; } void m3(int a, float b) { std::cout << "m3(a:" << y + a << ", b:" << y + b << ")" << std::endl; } int m4(void) { std::cout << "m4(): "; return ((int) y) + 4; } Object m5(int a) { std::cout << "m5(a:" << y + a << "): "; return Object(y + 5.1); } float m6(int a, Object b) { std::cout << "m6(a:" << y + a << ", b:" << y + b.y << "); "; return ((int) y) + 6.2f; } void c1(void) const { std::cout << "c1()" << std::endl; } void c2(int a) const { std::cout << "c2(a:" << y + a << ")" << std::endl; } void c3(int a, float b) const { std::cout << "c3(a:" << y + a << ", b:" << y + b << ")" << std::endl; } int c4(void) const { std::cout << "c4(): "; return ((int) y) + 4; } Object c5(int a) const { std::cout << "c5(a:" << y + a << "): "; return Object(y + 5.1); } float c6(int a, Object b) const { std::cout << "c6(a:" << y + a << ", b:" << y + b.y << "); "; return ((int) y) + 6.2f; } }; void f1(void) { std::cout << "f1()" << std::endl; } void f2(int a) { std::cout << "f2(a:" << a << ")" << std::endl; } void f3(int a, float b) { std::cout << "f3(a:" << a << ", b:" << b << ")" << std::endl; } int f4(void) { std::cout << "f4(): "; return 4; } Object f5(int a) { std::cout << "f5(a:" << a << "): "; return Object(5.1); } float f6(int a, Object b) { std::cout << "f6(a:" << a << ", b:" << b.y << "); "; return 6.2f; }
使用示例
int main() { std::cout << "=== Global functions" << std::endl; EventHandler ef1(f1); ef1(); EventHandler ef2(f2); ef2(2); EventHandler ef3(f3); ef3(3, 3.1f); EventHandler ef4(f4); std::cout << ef4() << std::endl; EventHandler ef5(f5); std::cout << ef5(5).y << std::endl; EventHandler ef6(f6); std::cout << ef6(6, Object(6.1)) << std::endl; std::cout << std::endl; std::cout << "=== Member static functions" << std::endl; EventHandler es1(Object::s1); es1(); EventHandler es2(Object::s2); es2(2); EventHandler es3(Object::s3); es3(3, 3.1f); EventHandler es4(Object::s4); std::cout << es4() << std::endl; EventHandler es5(Object::s5); std::cout << es5(5).y << std::endl; EventHandler es6(Object::s6); std::cout << es6(6, Object(6.1)) << std::endl; std::cout << std::endl; std::cout << "=== Member functions" << std::endl; Object object(20); EventHandler em1(&object, &Object::m1); em1(); EventHandler em2(&object, &Object::m2); em2(2); EventHandler em3(&object, &Object::m3); em3(3, 3.1f); EventHandler em4(&object, &Object::m4); std::cout << em4() << std::endl; EventHandler em5(&object, &Object::m5); std::cout << em5(5).y << std::endl; EventHandler em6(&object, &Object::m6); std::cout << em6(6, Object(6.1)) << std::endl; std::cout << std::endl; std::cout << "=== Member const functions" << std::endl; const Object constObject(30); EventHandler ec1(&constObject, &Object::c1); ec1(); EventHandler ec2(&constObject, &Object::c2); ec2(2); EventHandler ec3(&constObject, &Object::c3); ec3(3, 3.1f); EventHandler ec4(&constObject, &Object::c4); std::cout << ec4() << std::endl; EventHandler ec5(&constObject, &Object::c5); std::cout << ec5(5).y << std::endl; EventHandler ec6(&constObject, &Object::c6); std::cout << ec6(6, Object(6.1)) << std::endl; system("pause"); return 0; }
与std::function对比示例
EventHandler<float, int, Object> example; example = f6; example(7, Object(7.1)); example = EventHandler(&object, &Object::m6);; example(8, Object(8.1));
上述示例展示了EventHandler相较于std::function更简便的使用方式,这也是采用该实现方式的原因。
转换方式的合法性问题
这种转换完全不符合C++标准,属于未定义行为,具体问题包括:
成员函数指针的跨类型转换
C++标准仅允许有限的成员函数指针转换场景:比如派生类的非const成员函数指针可以转换为基类的对应成员函数指针,且基类必须是可访问、无歧义的。而你的代码中,EventHandler::Class和实际的Owner类型完全无关,这种强制转换没有任何标准依据,编译器可以拒绝编译,也可能生成不可靠的代码。const成员函数指针转非const
在处理const成员函数时,你把ReturnType (Owner::*)(Arguments...) const强制转换成ReturnType (Class::*)(Arguments...),这直接破坏了成员函数的const属性。调用时,原本承诺不修改对象的const成员函数,会被当作非const成员函数执行,可能导致对const对象的非法修改,触发未定义行为。对象指针的跨类型转换
将Owner*或const Owner*强制转换为Class*,同样属于未定义行为。虽然多数编译器会直接按内存地址转换,但如果两种类型的对齐要求不同,或者类型布局存在差异,转换后的指针访问会导致内存错误或数据损坏。
为什么当前测试能工作?
你的测试代码能运行,完全依赖编译器对成员函数指针的具体实现:很多编译器会把成员函数指针存储为函数地址(或偏移量),当函数签名一致时,强制转换后调用可能碰巧匹配正确的地址。但这种行为没有标准保证,更换编译器、修改编译选项(比如开启严格类型检查)、或者在更复杂的类结构(比如多重继承、虚函数)下,代码会立刻崩溃或出现异常行为。
合法的替代方案
如果想实现类似的简化调用体验,同时保证标准合规,可以采用以下方式:
使用类型擦除的封装函数
用void*存储对象指针,配合一个封装函数指针,把成员函数调用转换成普通函数调用:template <typename ReturnType, typename... Args> class EventHandler { private: using FuncPtr = ReturnType(*)(void*, Args...); void* obj = nullptr; FuncPtr call_func = nullptr; template <typename Owner> static ReturnType non_const_member_call(void* obj, Args... args) { auto mem_func = reinterpret_cast<ReturnType(Owner::*)(Args...)>(call_func); return (static_cast<Owner*>(obj)->*mem_func)(std::forward<Args>(args)...); } template <typename Owner> static ReturnType const_member_call(void* obj, Args... args) { auto mem_func = reinterpret_cast<ReturnType(Owner::*)(Args...) const>(call_func); return (static_cast<const Owner*>(obj)->*mem_func)(std::forward<Args>(args)...); } public: // 全局/静态函数构造 EventHandler(ReturnType(*func)(Args...)) : call_func(reinterpret_cast<FuncPtr>(func)) {} // 非const成员函数构造 template <typename Owner> EventHandler(Owner* obj, ReturnType(Owner::*mem_func)(Args...)) : obj(obj), call_func(reinterpret_cast<FuncPtr>(non_const_member_call<Owner>)) {} // const成员函数构造 template <typename Owner> EventHandler(const Owner* obj, ReturnType(Owner::*mem_func)(Args...) const) : obj(const_cast<void*>(static_cast<const void*>(obj))), call_func(reinterpret_cast<FuncPtr>(const_member_call<Owner>)) {} ReturnType operator()(Args... args) const { return call_func(obj, std::forward<Args>(args)...); } };基于
std::function封装
虽然std::function的语法稍繁琐,但可以通过模板构造函数简化调用,且兼容所有可调用对象(包括lambda、仿函数等):template <typename ReturnType, typename... Args> class EventHandler { private: std::function<ReturnType(Args...)> func; public: // 兼容全局函数、lambda、仿函数等 template <typename F> EventHandler(F&& f) : func(std::forward<F>(f)) {} // 非const成员函数绑定 template <typename Owner> EventHandler(Owner* obj, ReturnType(Owner::*mem_func)(Args...)) : func([obj, mem_func](Args... args) { return (obj->*mem_func)(std::forward<Args>(args)...); }) {} // const成员函数绑定 template <typename Owner> EventHandler(const Owner* obj, ReturnType(Owner::*mem_func)(Args...) const) : func([obj, mem_func](Args... args) { return (obj->*mem_func)(std::forward<Args>(args)...); }) {} ReturnType operator()(Args... args) const { return func(std::forward<Args>(args)...); } };
内容的提问来源于stack exchange,提问作者no one special

