Función que toma el puntero al método independientemente de la constancia
Quiero implementar una función genérica que hará referencia al objeto y apuntará a su función miembro y la invocará. Sin embargo, no puedo hacerlo cuando mi clase tiene métodos constantes y no constantes, ya que necesito proporcionar dos sobrecargas:
template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...))
{
return (object.*method)(Us{}...);
}
template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...) const)
{
return (object.*method)(Us{}...);
}
¿Hay alguna forma de escribir solo 1 función de plantilla que acepte punteros de método constantes y no constantes para que no tenga que escribir mi código dos veces? Estoy usando C++ 14.
Para una imagen más amplia, lo que quiero lograr en última instancia es pasar un tercer parámetro, un búfer de datos desde el cual se extraerán los argumentos del método, de ahí la función de plantilla para manejarlo de la manera más genérica posible.
Respuestas
Aquí hay una alternativa de C++ 14 sin usar std::function
.
lo que quiero lograr en última instancia es pasar un tercer parámetro, un búfer de datos desde el cual se extraerán los argumentos del método, de ahí la función de plantilla para manejarlo de la manera más genérica posible
Lo que utilices en el sitio de la llamada se reenviará perfectamente aquí:
template<typename Class, typename Func, typename... Args>
decltype(auto) callMethod_impl(Class& object, Func method, Args&&... args) {
return (object.*method)(std::forward<Args>(args)...);
}
template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(Class& object, Ret(Class::*method)(Us...), Args&&... args) {
return callMethod_impl(object, method, std::forward<Args>(args)...);
}
template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(const Class& object, Ret(Class::*method)(Us...) const, Args&&... args) {
return callMethod_impl(object, method, std::forward<Args>(args)...);
}
Manifestación
Si lo necesita Ret
, callMethod_impl
simplemente agréguelo como un parámetro de plantilla y llámelo como callMethod_impl<Ret>(...)
desde las callMethod
sobrecargas ( Demo ).
La respuesta corta es, no implemente esto usted mismo, ya se ha hecho por usted en forma de std::invoke:
#include <functional>
struct A {
void foo(int x);
void bar(int x) const;
};
void example() {
A a;
std::invoke(&A::foo, a, 3);
std::invoke(&A::bar, a, 3);
}
Al ver que ha agregado una etiqueta C++ 14 en retrospectiva, la documentación de std::invoketiene una implementación de muestra que puede usar en su proyecto.
Sin std::invoke
, solo puede realizar algunas soluciones alternativas más o menos genéricas, según el tipo de borrado. El uso std::function
le permite crear una función de proxy para tratar sus funciones en igualdad de condiciones:
#include <iostream>
#include <functional>
template<typename Ret, typename ...Us>
Ret callMethod_impl(std::function<Ret(Us...)> f) {
// common implementation here
return f(Us{}...);
}
template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...)) {
return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}
template<typename Ret, typename Class, typename ...Us>
Ret callMethod(const Class &object, Ret (Class::*method)(Us...) const) {
return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}
struct Foo {
int bar() const { return 1; }
float bar() { return 2.1f; };
};
int main() {
Foo f;
std::cout << callMethod(f, &Foo::bar) << std::endl;
}
Tenga en cuenta que volatile
las funciones (+ combinaciones) no se tratan aquí, pero tal vez no necesite una generalidad completa sino una solución pragmática.