Fonction prenant le pointeur vers la méthode indépendamment de la constance
Je souhaite implémenter une fonction générique qui prendra référence à l'objet et pointera vers sa fonction membre et l'invoquera. Cependant, je ne peux pas le faire lorsque ma classe a à la fois des méthodes const et non const car je dois fournir deux surcharges :
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{}...);
}
Existe-t-il un moyen d'écrire une seule fonction modèle qui acceptera les pointeurs de méthode const et non const afin que je n'aie pas à écrire mon code deux fois? J'utilise C++14.
Pour une image plus large, ce que je veux finalement réaliser est de passer un 3ème paramètre, un tampon de données à partir duquel les arguments de méthode seront extraits - d'où la fonction de modèle pour le gérer de manière aussi générique que possible.
Réponses
Voici une alternative C++14 sans utiliser std::function
.
ce que je veux finalement réaliser est de passer un 3ème paramètre, un tampon de données à partir duquel les arguments de méthode seront extraits - d'où la fonction de modèle pour le gérer de manière aussi générique que possible
Ce que vous utilisez sur le site d'appel sera parfaitement transmis ici :
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)...);
}
Démo
Si vous avez besoin Ret
de in callMethod_impl
, ajoutez-le simplement en tant que paramètre de modèle et appelez-le comme à callMethod_impl<Ret>(...)
partir des callMethod
surcharges ( Demo ).
La réponse courte est, ne l'implémentez pas vous-même, cela a déjà été fait pour vous sous la forme 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);
}
Étant donné que vous avez ajouté une balise C++14 rétrospectivement, la documentation de std::invokecontient un exemple d'implémentation que vous pouvez utiliser dans votre projet.
Sans std::invoke
, vous ne pouvez effectuer que des solutions de contournement plus ou moins génériques, basées sur l'effacement de type. L'utilisation std::function
vous permet de créer une fonction proxy pour traiter vos fonctions sur un pied d'égalité :
#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;
}
Notez que volatile
les fonctions (+ combinaisons) ne sont pas traitées ici, mais peut-être n'avez-vous pas besoin d'une généralité complète mais plutôt d'une solution pragmatique.