c++ 成员函数指针的正确处理

knpiaxh1  于 2022-12-20  发布在  其他
关注(0)|答案(1)|浏览(127)

我写了一个泛型类来处理和执行函数指针。这是std::functionstd::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成员函数的句柄。显然,为了清楚起见,这里没有显示volatileconst 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;
}

最后--说到点子上--这里有一个例子,显示了我准备的EventHandler与std::function接口相比是多么容易使用,以及使用这种方法的真正原因。

EventHandler<float, int, Object> example;

    example = f6;
    example(7, Object(7.1));

    example = EventHandler(&object, &Object::m6);;
    example(8, Object(8.1));
xzlaal3s

xzlaal3s1#

通过函数指针调用函数是未定义的行为不同类型的(-to-member)。(此规则的一些实际原因是,可能需要调整对象的地址以调用基类的成员函数,或者可能涉及vtable。)可以使用type erasure来允许调用不同类型的对象上的成员函数(这是std::bind所做的),或者您可以(限制为成员函数和)添加类类型作为模板参数
当然,通常的答案是使用带有lambdastd::function,它捕获所讨论的对象并调用任何成员函数。您也可以采用C方法并定义带有**void***参数的各种函数,这些函数将该参数强制转换为已知的类类型并调用所需的成员函数。

相关问题