Fonction prenant le pointeur vers la méthode indépendamment de la constance

Aug 19 2020

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

2 TedLyngmo Aug 19 2020 at 19:58

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 Retde in callMethod_impl, ajoutez-le simplement en tant que paramètre de modèle et appelez-le comme à callMethod_impl<Ret>(...)partir des callMethodsurcharges ( Demo ).

3 JanSchultke Aug 19 2020 at 17:28

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.

1 Jodocus Aug 19 2020 at 17:59

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::functionvous 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 volatileles 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.