有没有一种通用的方法来传递需要更少工作的重载方法的指针(比我的例子)
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 的现代信号和槽。
所以我有一个函数,使用 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 的现代信号和槽。