有没有一种通用的方法来传递需要更少工作的重载方法的指针(比我的例子)

Is there a generic way to pass pointers of overloaded methods that requires less work (than my example)

所以我有一个函数,使用 C++17,我能够从任何对象应用任何方法:

#include <functional>

template <typename Object, typename Method, typename ... Args>
void ApplyMethod (Object && object, Method && method, Args && ... args)
{
    std::invoke(method, object, args...);
}

我想问的是:有没有办法改进这一点,以便在方法重载时减少函数调用者的工作量。

使用重载方法的示例:

#include <iostream>

class Foo
{
    int bottles;

public:

    void Edit ()
    {
        bottles = 666;
    }

    void Edit (int number)
    {
        bottles = number;
    }
    
    void Talk () const
    {
        std::cout << bottles << " bottles of beer of the wall" << std::endl;
    }
};

class Bar
{
    Foo foo;
    
    void TrickEdit (int number)
    {
        // Because Foo::Edit is overloaded, we need to do some work:
    
        using Method = void (Foo::*)(int);
    
        Method ptr = &Foo::Edit;
        
        ApplyMethod(foo, ptr, number);
    }
    
    void TrickTalk () const
    {
        // it's a lot neater when the method isn't overloaded:
    
        ApplyMethod(foo, &Foo::Talk);
    }

public:

    void Trick ()
    {
        TrickEdit(900);
        TrickTalk();    
    }   
};


int main ()
{
    Bar().Trick();
    
    return 0;
}

我正在尝试在函数中执行工作。问题似乎在于 &Foo::Edit 有两个不同的位置,具体取决于我们所指的 Edit

在 Stroustrup 和其他著名作者的 C++ FAQ - Pointers to member functions 中,我读到:

Question: I need something like function-pointers, but with more flexibility and/or thread-safety; is there another way?

Answer: Use a functionoid.

Question: What the heck is a functionoid, and why would I use one?

Answer: Functionoids are functions on steroids. Functionoids are strictly more powerful than functions, and that extra power solves some (not all) of the challenges typically faced when you use function-pointers. [...] Functionoids don’t solve every problem encountered when making flexible software, but they are strictly more powerful than function-pointers and they are worth at least evaluating. In fact you can easily prove that functionoids don’t lose any power over function-pointers, since you can imagine that the old-fashioned approach of function-pointers is equivalent to having a global(!) functionoid object. Since you can always make a global functionoid object, you haven’t lost any ground. QED.

鉴于编程的“力量”基本上是减少工作重复,并且使用正常功能,我们将避免我在问题中概述的调用站点的额外工作,FAQ 答案暗示应该有一个解决方案,使用 functionoids。然而,对于我的生活,我看不出 functionoids 在这种情况下会有什么帮助。

也许你可以使用类似的东西:

struct A
{   
    void Do() { std::cout << "Do no parm" << std::endl; }
    void Do(int) { std::cout << "Do 1 parm" << std::endl; }
    void Do(int,int) { std::cout << "Do 2 parms" << std::endl; }
};  

template < typename OBJ_TYPE, typename ... ARGS >
auto Invoke( OBJ_TYPE&& obj, void( std::remove_reference<OBJ_TYPE>::type::* func)(ARGS...), ARGS&& ... args )
{   
    return std::invoke( func, obj, args... );
}   

int main()
{   
    A a;
    Invoke( a, &A::Do);
    Invoke( a, &A::Do, 1); 
    Invoke( a, &A::Do,1,2);
}

思路很简单,将成员函数指针的指针类型固定为参数包中给定的实参。

如果有人知道如何自动确定 return 类型,以便我们也可以使用不同 return 类型的重载,那将是非常有趣的!我结束了递归:-)

如果我们简单的指定return类型,我们可以这样使用:

struct A
{   
    void Do() { std::cout << "Do no parm" << std::endl; }
    void Do(int) { std::cout << "Do 1 parm" << std::endl; }
    int Do(int,int) { std::cout << "Do 2 parms" << std::endl; return 42;}
};  


template < typename RETURN_TYPE, typename OBJ_TYPE, typename ... ARGS >
auto Invoke( OBJ_TYPE&& obj, RETURN_TYPE( std::remove_reference<OBJ_TYPE>::type::* func)(ARGS...), ARGS&& ... args )
{   
    return std::invoke( func, obj, args... );
}   

int main()
{   
    A a;
    Invoke<void>( a, &A::Do);
    Invoke<void>( a, &A::Do, 1); 
    int retval = Invoke<int>( a, &A::Do,1,2);
    std::cout << retval << std::endl;
}

您可以编写一个变量模板来指定 Args... 应该是什么。

template <typename... Args>
struct Overload {
    template<typename R, typename O>
    operator R(O::*)(Args...) (R(O::*p)(Args...)) const { return p; }
    template<typename R, typename O>
    operator R(O::*)(Args...) const (R(O::*p)(Args...) const) const { return p; }
};

template <typename... Args>
Overload overload;

哪个用得像

struct A
{   
    void Do() { std::cout << "Do no parm" << std::endl; }
    void Do(int) { std::cout << "Do 1 parm" << std::endl; }
    void Do(int,int) { std::cout << "Do 2 parms" << std::endl; }
};  

template <typename Object, typename Method, typename ... Args>
void ApplyMethod (Object && object, Method && method, Args && ... args)
{
    std::invoke(method, object, args...);
}

int main()
{   
    A a;
    ApplyMethod( a, overload<>(&A::Do));
    ApplyMethod( a, overload<int>(&A::Do), 1); 
    ApplyMethod( a, overload<int, int>(&A::Do),1,2);
}

这就是 Qt does 的现代信号和槽。