Funzione che accetta il puntatore al metodo indipendentemente dalla costanza

Aug 19 2020

Voglio implementare una funzione generica che faccia riferimento all'oggetto e al puntatore alla sua funzione membro e la richiami. Tuttavia non sono in grado di farlo quando la mia classe ha entrambi i metodi const e non const poiché devo fornire due sovraccarichi:

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{}...);
}

C'è un modo per scrivere solo 1 funzione modello che accetterà puntatori di metodo sia const che non const, quindi non devo scrivere il mio codice due volte? Sto usando C++ 14.

Per un quadro più ampio, ciò che voglio ottenere alla fine è passare un terzo parametro, un buffer di dati da cui verranno estratti gli argomenti del metodo, da cui la funzione template per gestirlo nel modo più generico possibile.

Risposte

2 TedLyngmo Aug 19 2020 at 19:58

Ecco un'alternativa C++14 senza usare std::function.

quello che voglio ottenere alla fine è passare un terzo parametro, un buffer di dati da cui verranno estratti gli argomenti del metodo, quindi la funzione template per gestirlo nel modo più generico possibile

Ciò che utilizzi nel sito di chiamata verrà inoltrato perfettamente qui:

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)...);
}

Demo

Se hai bisogno Retin callMethod_impl, aggiungilo come parametro del modello e chiamalo come callMethod_impl<Ret>(...)dagli callMethodoverload ( Demo ).

3 JanSchultke Aug 19 2020 at 17:28

La risposta breve è, non implementarlo da solo, è già stato fatto per te sotto forma di 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); 
}

Visto che hai aggiunto un tag C++ 14 in retrospettiva, la documentazione di std::invokeha un'implementazione di esempio che puoi usare nel tuo progetto.

1 Jodocus Aug 19 2020 at 17:59

Senza std::invoke, puoi solo eseguire alcune soluzioni alternative più o meno generiche, basate sulla cancellazione del tipo. L'utilizzo std::functionti consente di creare una funzione proxy per trattare le tue funzioni su un piano di parità:

#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;
}

Nota che volatilele funzioni (+ combinazioni) non sono trattate qui, ma forse non hai bisogno di una generalità completa ma piuttosto di una soluzione pragmatica.