Puntatore a funzione alla funzione membro


92

Vorrei impostare un puntatore a funzione come membro di una classe che è un puntatore a un'altra funzione nella stessa classe. I motivi per cui lo sto facendo sono complicati.

In questo esempio, vorrei che l'output fosse "1"

class A {
public:
 int f();
 int (*x)();
}

int A::f() {
 return 1;
}


int main() {
 A a;
 a.x = a.f;
 printf("%d\n",a.x())
}

Ma questo non riesce a compilare. Perché?



@jww e controlla la risposta di CiroSantilli a quella domanda, le altre risposte sono più o meno fuori tema. Fondamentalmente, solo int (C :: * function_pointer_var) (int) = & C :: method; quindi C c; e (c. * function_pointer_var) (2).
jw_

Risposte:


160

La sintassi è sbagliata. Un puntatore a un membro è una categoria di tipo diversa da un normale puntatore. Il puntatore del membro dovrà essere utilizzato insieme a un oggetto della sua classe:

class A {
public:
 int f();
 int (A::*x)(); // <- declare by saying what class it is a pointer to
};

int A::f() {
 return 1;
}


int main() {
 A a;
 a.x = &A::f; // use the :: syntax
 printf("%d\n",(a.*(a.x))()); // use together with an object of its class
}

a.xnon dice ancora su quale oggetto deve essere chiamata la funzione. Dice solo che vuoi usare il puntatore memorizzato nell'oggetto a. Anteporre aun'altra volta come operando sinistro .*all'operatore dirà al compilatore su quale oggetto chiamare la funzione.


So che è vecchio, ma non capisco l'uso di. (a.*a.x)()Perché (a.*x)()non funziona?
Gaurav Sehgal

3
@gau perché x non rientra nell'ambito
Johannes Schaub - litb

13
Devo cercare anche questo ogni volta che lo uso. La sintassi è confusa, ma ha senso se la scomponi. a.xè un puntatore a una funzione membro di classe A. *a.xdereferenzia il puntatore, quindi ora è un riferimento a una funzione. a.(*a.x)"lega" la funzione a un'istanza (proprio come a.f). (a.(*a.x))è necessario per raggruppare questa sintassi complessa e (a.(*a.x))()invoca effettivamente il metodo su asenza argomenti.
jwm

23

int (*x)()non è un puntatore alla funzione membro. Un puntatore a funzione membro è scritto così: int (A::*x)(void) = &A::f;.


17

Chiama la funzione membro sul comando stringa

#include <iostream>
#include <string>


class A 
{
public: 
    void call();
private:
    void printH();
    void command(std::string a, std::string b, void (A::*func)());
};

void A::printH()
{
    std::cout<< "H\n";
}

void A::call()
{
    command("a","a", &A::printH);
}

void A::command(std::string a, std::string b, void (A::*func)())
{
    if(a == b)
    {
        (this->*func)();
    }
}

int main()
{
    A a;
    a.call();
    return 0;
}

Prestare attenzione (this->*func)();e al modo in cui dichiarare il puntatore a funzione con il nome della classevoid (A::*func)()


11

È necessario utilizzare un puntatore a una funzione membro, non solo un puntatore a una funzione.

class A { 
    int f() { return 1; }
public:
    int (A::*x)();

    A() : x(&A::f) {}
};

int main() { 
   A a;
   std::cout << (a.*a.x)();
   return 0;
}

3

Sebbene questo sia basato sulle risposte sterili in altre parti di questa pagina, ho avuto un caso d'uso che non è stato completamente risolto da loro; per un vettore di puntatori a funzioni, procedi come segue:

#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>

class A{
public:
  typedef vector<int> (A::*AFunc)(int I1,int I2);
  vector<AFunc> FuncList;
  inline int Subtract(int I1,int I2){return I1-I2;};
  inline int Add(int I1,int I2){return I1+I2;};
  ...
  void Populate();
  void ExecuteAll();
};

void A::Populate(){
    FuncList.push_back(&A::Subtract);
    FuncList.push_back(&A::Add);
    ...
}

void A::ExecuteAll(){
  int In1=1,In2=2,Out=0;
  for(size_t FuncId=0;FuncId<FuncList.size();FuncId++){
    Out=(this->*FuncList[FuncId])(In1,In2);
    printf("Function %ld output %d\n",FuncId,Out);
  }
}

int main(){
  A Demo;
  Demo.Populate();
  Demo.ExecuteAll();
  return 0;
}

Qualcosa di simile è utile se stai scrivendo un interprete di comandi con funzioni indicizzate che devono essere sposate con la sintassi dei parametri e suggerimenti di aiuto, ecc. Forse anche utile nei menu.


1
Come definito, AFunc è un puntatore a una funzione membro che prende due int e restituisce un vettore di int. Ma i membri hanno indicato di restituire int, giusto? Penso che la dichiarazione typedef dovrebbe essere typedef int (A::*AFunc)(int I1,int I2);
riderBill

3

Anche se sfortunatamente non è possibile convertire un puntatore a funzione membro esistente in un puntatore a funzione semplice, è possibile creare un modello di funzione adattatore in un modo abbastanza semplice che avvolge un puntatore a funzione membro noto in fase di compilazione in una funzione normale come questa:

template <class Type>
struct member_function;

template <class Type, class Ret, class... Args>
struct member_function<Ret(Type::*)(Args...)>
{
    template <Ret(Type::*Func)(Args...)>
    static Ret adapter(Type &obj, Args&&... args)
    {
        return (obj.*Func)(std::forward<Args>(args)...);
    }
};

template <class Type, class Ret, class... Args>
struct member_function<Ret(Type::*)(Args...) const>
{
    template <Ret(Type::*Func)(Args...) const>
    static Ret adapter(const Type &obj, Args&&... args)
    {
        return (obj.*Func)(std::forward<Args>(args)...);
    }
};

 

int (*func)(A&) = &member_function<decltype(&A::f)>::adapter<&A::f>;

Si noti che per chiamare la funzione membro, è Anecessario fornire un'istanza di .


Mi hai ispirato, @ IllidanS4. Vedi la mia risposta. +1
memtha

1

Basandosi sulla risposta di @ IllidanS4, ho creato una classe modello che consente di passare per riferimento virtualmente qualsiasi funzione membro con argomenti predefiniti e istanza di classe per una chiamata successiva.



template<class RET, class... RArgs> class Callback_t {
public:
    virtual RET call(RArgs&&... rargs) = 0;
    //virtual RET call() = 0;
};

template<class T, class RET, class... RArgs> class CallbackCalltimeArgs : public Callback_t<RET, RArgs...> {
public:
    T * owner;
    RET(T::*x)(RArgs...);
    RET call(RArgs&&... rargs) {
        return (*owner.*(x))(std::forward<RArgs>(rargs)...);
    };
    CallbackCalltimeArgs(T* t, RET(T::*x)(RArgs...)) : owner(t), x(x) {}
};

template<class T, class RET, class... Args> class CallbackCreattimeArgs : public Callback_t<RET> {
public:
    T* owner;
    RET(T::*x)(Args...);
    RET call() {
        return (*owner.*(x))(std::get<Args&&>(args)...);
    };
    std::tuple<Args&&...> args;
    CallbackCreattimeArgs(T* t, RET(T::*x)(Args...), Args&&... args) : owner(t), x(x),
        args(std::tuple<Args&&...>(std::forward<Args>(args)...)) {}
};

Test / esempio:

class container {
public:
    static void printFrom(container* c) { c->print(); };
    container(int data) : data(data) {};
    ~container() {};
    void print() { printf("%d\n", data); };
    void printTo(FILE* f) { fprintf(f, "%d\n", data); };
    void printWith(int arg) { printf("%d:%d\n", data, arg); };
private:
    int data;
};

int main() {
    container c1(1), c2(20);
    CallbackCreattimeArgs<container, void> f1(&c1, &container::print);
    Callback_t<void>* fp1 = &f1;
    fp1->call();//1
    CallbackCreattimeArgs<container, void, FILE*> f2(&c2, &container::printTo, stdout);
    Callback_t<void>* fp2 = &f2;
    fp2->call();//20
    CallbackCalltimeArgs<container, void, int> f3(&c2, &container::printWith);
    Callback_t<void, int>* fp3 = &f3;
    fp3->call(15);//20:15
}

Ovviamente, questo funzionerà solo se gli argomenti e la classe del proprietario forniti sono ancora validi. Per quanto riguarda la leggibilità ... ti prego di perdonarmi.

Modifica: rimosso malloc non necessario rendendo la tupla normale archiviazione. Aggiunto tipo ereditato per il riferimento. Aggiunta opzione per fornire invece tutti gli argomenti al momento della chiamata. Ora sto lavorando per avere entrambi ...

Modifica 2: come promesso, entrambi. L'unica limitazione (che vedo) è che gli argomenti predefiniti devono precedere gli argomenti forniti dal runtime nella funzione di callback. Grazie a @Chipster per l'aiuto con la conformità a gcc. Funziona su gcc su Ubuntu e su Visual Studio su Windows.

#ifdef _WIN32
#define wintypename typename
#else
#define wintypename
#endif

template<class RET, class... RArgs> class Callback_t {
public:
    virtual RET call(RArgs... rargs) = 0;
    virtual ~Callback_t() = default;
};

template<class RET, class... RArgs> class CallbackFactory {
private:
    template<class T, class... CArgs> class Callback : public Callback_t<RET, RArgs...> {
    private:
        T * owner;
        RET(T::*x)(CArgs..., RArgs...);
        std::tuple<CArgs...> cargs;
        RET call(RArgs... rargs) {
            return (*owner.*(x))(std::get<CArgs>(cargs)..., rargs...);
        };
    public:
        Callback(T* t, RET(T::*x)(CArgs..., RArgs...), CArgs... pda);
        ~Callback() {};
    };
public:
    template<class U, class... CArgs> static Callback_t<RET, RArgs...>* make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...));
};
template<class RET2, class... RArgs2> template<class T2, class... CArgs2> CallbackFactory<RET2, RArgs2...>::Callback<T2, CArgs2...>::Callback(T2* t, RET2(T2::*x)(CArgs2..., RArgs2...), CArgs2... pda) : x(x), owner(t), cargs(std::forward<CArgs2>(pda)...) {}
template<class RET, class... RArgs> template<class U, class... CArgs> Callback_t<RET, RArgs...>* CallbackFactory<RET, RArgs...>::make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)) {
    return new wintypename CallbackFactory<RET, RArgs...>::Callback<U, CArgs...>(owner, func, std::forward<CArgs>(cargs)...);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.