Funzione che accetta il puntatore al metodo indipendentemente dalla costanza
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
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 Ret
in callMethod_impl
, aggiungilo come parametro del modello e chiamalo come callMethod_impl<Ret>(...)
dagli callMethod
overload ( Demo ).
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.
Senza std::invoke
, puoi solo eseguire alcune soluzioni alternative più o meno generiche, basate sulla cancellazione del tipo. L'utilizzo std::function
ti 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 volatile
le funzioni (+ combinazioni) non sono trattate qui, ma forse non hai bisogno di una generalità completa ma piuttosto di una soluzione pragmatica.