2016-02-10 27 views
6

Capisco il problema di base nel passare l'indirizzo di una funzione membro al di fuori della sua classe. Ho la sensazione che mem_fn() potrebbe essere la soluzione, ma ho problemi con le specifiche.Come dichiarare un mem_fn C++ (membro_funzione) nella dichiarazione di funzione?

Ho una funzione membro della classe p che è attualmente dichiarato come

typedef void (*valNamedFlagsCallback)(const int, const bool); 
bool valNamedFlags(const OptBlk *operand, const char *description_of_value_or_NULL, const int subscripts[], const char *names[], valNamedFlagsCallback callBack); 

In classe e sto provando a chiamare valNamedFlags con

pInstance->valNamedFlags(operand, "Statement types", statementsSubscripts, statementsNames, std::mem_fn(&e::setStatement)); 

(ho iniziato senza la mem_fn() ma, naturalmente, questo ha il classico problema di "puntatori alle funzioni degli utenti". Ho provato sia & e :: setStatement che semplicemente & setStatement.)

.210

FWIW, setStatement è prototipo come

void setStatement(const int ifcid, const bool isAffirmative); 

Tutto funziona se elimino il mem_fn() e dichiaro setStatement come statica. Lo sto solo indicando come un modo per dire che ho eliminato tutti gli altri possibili problemi; il mio unico problema è il problema dei "puntatori alle funzioni dei membri". Sfortunatamente, setStatement() deve essere una funzione membro, non una statica.

L'errore specifico che sto ottenendo in MS VS 2010 è

bool p :: valNamedFlags (const OptBlk *, const char *, const int [], const char * [], p :: valNamedFlagsCallback)': non può convertire il parametro 5 da 'std :: tr1 :: _ Mem_fn3 < _Rx, _Pmf, _Arg0, _Arg1, _Arg2>' a 'p :: valNamedFlagsCallback'

Vorrei ottenere la dichiarazione di callback indipendenti di classe e; cioè, non voglio andare a

typedef void (*e::valNamedFlagsCallback)(const int, const bool); 

perché voglio mantenere p più generalizzato di quello.

È mem_fn() la soluzione giusta o la mia base di partenza? In tal caso, come dovrei dichiarare la richiamata nel prototipo valNamedFlags()?

Oppure devo seguire un approccio diverso?

+0

Dai un'occhiata a ['std :: bind'] (http://en.cppreference.com/w/cpp/utility/functional/bind) – StoryTeller

+0

^... e' std :: function' quando ci siamo già. –

+1

Grazie. Ho dato un'occhiata a molte pagine Web e la mia testa sta nuotando. Ho bisogno di alcuni dettagli. Ecco perché sono venuto qui con questa domanda. – Charles

risposta

6

In realtà, avrete un problema fare cose bind-come come qualcuno ha suggerito, dal momento che il callback viene definito come un semplice puntatore a funzione, e non un callable.

Se si può permettersi di farlo, è possibile modificare la

typedef void (*valNamedFlagsCallback)(const int, const bool); 

a qualcosa di simile

typedef std::function<void(int, bool)> valNamedFlagsCallback 

(anche notare che const su parametri di valore non influisce la firma della funzione), quindi è possibile utilizzare std::bind(),

using namespace std::placeholders; 

pInstance->valNamedFlags(operand, 
     "Statement types", 
     statementsSubscripts, 
     statementsNames, 
     std::bind(&E::setStatement, e, _1, _2)); 

o yo u possibile utilizzare lambda:

pInstance->valNamedFlags(operand, 
    "Statement types", 
    statementsSubscripts, 
    statementsNames, 
    [&](int i, bool b) { e->setStatement(i, b); }); 

Se è necessario tenerlo come una semplice funzione, allora si dovrà inviare una che fa riferimento all'oggetto destra come una variabile globale/static.

+0

Grazie mille! Questo è il livello di aiuto di cui ho bisogno. Ho dimenticato di dire che un problema è che ho trovato molti esempi di mem_fn() e bind() sul Web, ma nessuno è per un prototipo di funzione e non sono stato in grado di fare il salto da solo. Non riesco a capire perché * non * voglia andare alla tua versione std :: typedef. Non sono sicuro esattamente come specificare il riferimento ora nella chiamata. Provato std :: bind (& e :: setStatement) e sto ricevendo dozzine di errori imperscrutabili su modelli MS ... – Charles

+0

Oh, anche, sto facendo test alfa in MS VS 2010 ma il mio ambiente finale è un compilatore che offre solo supporto parziale per C++ 11 e quindi non posso usare lambdas. – Charles

+0

@Charles Non è colpa tua, perché ho avuto un refuso.Prova di nuovo con il codice revisionato, per favore, e vedremo cos'altro deve essere risolto. –

5

È necessario bind un'istanza su di esso per chiamare tramite un puntatore funzione membro. (Ad esempio, std::bind(&e::setStatement, eInstance, _1, _2), supporre che eInstance sia un puntatore a un oggetto della classe e).

using namespace std::placeholders; // for _1, _2, _3... 
pInstance->valNamedFlags(operand, "Statement types", statementsSubscripts, statementsNames, std::bind(&e::setStatement, eInstance, _1, _2)); 

noti che il valore di ritorno di std::bind (che è specificato) non corrisponde alla funzione libera tipo puntatore valNamedFlagsCallback, una delle soluzioni è quello di utilizzare std::function.

typedef std::function<void(const int, const bool)> valNamedFlagsCallback; 

Simplified demo

+1

In realtà, '' bind() 'restituirà qualcosa che è convertibile in un semplice puntatore a funzione, come lo ha definito lui? –

+2

@YamMarcovic - buon punto. 'valNamedFlags' deve prendere il risultato dell'espressione di bind come argomento finale; in pratica, ciò significa che dovrebbe essere o un modello o dovrebbe prendere 'std :: function ' dove attualmente richiede 'valNamedFlagsCallback'. –

+0

@YamMarcovic Hai ragione. – songyuanyao

0

Capito !!! RINGRAZIA TUTTI!!!

Prototipo:

bool valNamedFlags(const OptBlk *operand, const char *description_of_value_or_NULL, const int subscripts[], const char *names[], std::function<void(int,bool)> callBack); 

chiamata:

valNamedFlags(..., std::bind(&e::setStatement, this, _1, _2)); 

corre realtà e chiede setStatement con gli argomenti appropriati.

+0

Solo per completezza, aggiungo che MS VS 2010 prende std :: bind e così via senza problemi, ma il mio altro compilatore che è molto più di un stickler per la perfezione sintattica conforme agli standard richiede std :: tr1 :: bind() e così via. – Charles

Problemi correlati