2010-02-22 18 views
8

ho una mappa che memorizza un semplice struct con una chiave. La struct ha due funzioni membro, una è const l'altra no. Sono riuscito chiamando la funzione const utilizzando std :: for_each senza problemi, ma ho qualche problema chiamando la funzione non-const.Boost.Bind per accedere std :: elementi della mappa in std :: for_each

struct MyStruct { 
    void someConstFunction() const; 
    void someFunction(); 
}; 

typedef std::map<int, MyStruct> MyMap; 
MyMap theMap; 

//call the const member function 
std::for_each(theMap.begin(), theMap.end(), 
    boost::bind(&MyStruct::someConstFunction, boost::bind(&MyMap::value_type::second, _1))); 

//call the non-const member function 
std::for_each(theMap.begin(), theMap.end(), 
    boost::bind(&MyStruct::someFunction, boost::bind(&MyMap::value_type::second, _1))); 

La chiamata alla funzione membro const funziona bene, ma sembra aumentare internamente aspetta una MyStruct const da qualche parte, e, quindi, non riesce con il seguente errore di compilazione in MSVC7.1.

spinta \ bind \ mem_fn_template.hpp (151): l'errore C2440: 'argomento': non può convertire da 'MyStruct const * __ W64' a 'MyStruct * const'

Apprezzerei tutto aiuto su come impostare correttamente i parametri del modello, in modo legano riconosce i parametri in modo corretto e fammi chiamare la funzione non const.

grazie, Carl

+0

Che ne dici se si esegue il backup e ci dici cosa stai davvero cercando di realizzare qui?Usare for_each con una mappa con boost :: bind * potrebbe * essere ragionevole, ma è molto probabile che un approccio generale diverso funzioni meglio (molte volte questo tipo di domande sorge, è perché 'std :: for_each' è una scelta sbagliata per la situazione, e qualcosa come 'std :: copy' o std :: accumulate' farebbe il lavoro molto più semplicemente). –

+0

Il MyStruct è usato in una sorta di sistema particellare, in cui MyStruct è la particella. La funzione const è una funzione draw(), la funzione non const calcola la nuova posizione. La chiave nella mappa è la data di creazione. In ogni caso, nel punto in cui ho postato la domanda, era più su come fare quel lavoro che se questo fosse un buon progetto all'inizio. – Carl

risposta

8

IIRC, Boost.Bind utilizza boost::mem_fn per l'associazione ai membri. Ora, se guardi a mem_fun (scorri verso il basso fino alla parte // data member support), vedrai che digita il suo result_type come const &, mentre ha ancora sovraccarichi dell'operatore di chiamata di funzione che supporta l'estrazione di un membro non-const da un argomento non-const.

Sembra quindi che il problema sia che questo confonde il meccanismo di detrazione del tipo di ritorno di Boost.Bind. In tal caso, una soluzione dovrebbe dichiarare esplicitamente a Bind che il risultato non è const:

//call the non-const member function 
std::for_each(theMap.begin(), theMap.end(), 
    boost::bind(&MyStruct::someFunction, 
     boost::bind<MyStruct&>(&MyMap::value_type::second, _1) 
    ) 
); 
+0

+1 bel lavoro investigativo. :-) Stranamente, compilando 'boost :: lamba :: bind' si compila senza specificare esplicitamente il tipo di ritorno. Forse 'boost :: lamda :: bind' è più intelligente di' boost :: bind' nel dedurre i tipi di ritorno? –

+0

Wow, grazie molte. Questo si compila bene. Sebbene mi piaccia usare Boost, è ancora abbastanza difficile per me leggere la maggior parte del loro codice, quindi ho fallito. Grazie per l'aiuto. – Carl

+0

Penso che tu intenda 'boost :: mem_fn' – Manuel

0

Un problema che ho notato: il secondo BIND è chiamato per un membro non funzioni. secondo è un membro di dati, non è un metodo di std :: coppia

+3

Ho trovato questa tecnica in questo articolo: http://www.informit.com/articles/article.aspx?p=412354&seqNum=4 Si afferma "È possibile associare a una variabile membro proprio come è possibile con una funzione membro , o una funzione gratuita. ". Poiché il codice for_each è essenzialmente lo stesso per entrambe le funzioni membro e il problema si verifica solo nella chiamata alla funzione membro non const, suppongo che l'articolo sia corretto. – Carl

4

Se sei già dipendete Boost, si può essere disposti a controllare Boost Foreach

BOOST_FOREACH(MyMap::value_type const& val, MyMap) 
{ 
    val.second.someConstFunction(); 
} 

Molto molto leggibile, anche se non so su problemi di prestazioni.

Si noti inoltre che non è possibile utilizzare su modelli digitato all'interno della macro senza "fuga" il carattere ,:

  • sia da un typedef prima
  • o utilizzando una seconda coppia di parentesi intorno al tipo
+0

Conosco Boost Foreach e ovviamente funziona. Ma sono solo curioso di trovare la sintassi corretta per la soluzione di cui sopra, dal momento che il codice sopra funziona bene per la funzione const e fallisce per quello non const. – Carl

+4

Credo che il codice corretto sarebbe (rimuovere il const se si desidera modificare i valori): BOOST_FOREACH (MyMap :: value_type const & val, theMap) {...} – Bklyn

+0

e non è necessario collegare per aumentare quando usi Boost.Bind o Boost.Foreach –

7

Se vi trovate a dover fare questo molto vi consiglio di utilizzare la libreria Boost.RangeEx:

#include <boost/range/algorithm/for_each.hpp> 
#include <boost/range/adaptor/map.hpp> 
#include <boost/mem_fn.hpp> 
#include <map> 

struct MyStruct { 
    void someConstFunction() const; 
    void someFunction(); 
}; 

typedef std::map<int, MyStruct> MyMap; 
MyMap theMap; 

int main() 
{ 
    //call the const member function 
    boost::for_each(theMap | boost::adaptors::map_values, 
        boost::mem_fn(&MyStruct::someConstFunction)); 

    //call the non-const member function 
    boost::for_each(theMap | boost::adaptors::map_values, 
        boost::mem_fn(&MyStruct::someFunction)); 
} 

È stato accettato in Boost ma non è ancora stato distribuito ufficialmente. Fino a quando lo fa si può download it dalla (link per il download di file zip) Boost Vault.

+0

Sembra molto più leggibile e comprensibile rispetto alla soluzione Bind. Vale sicuramente la pena dare un'occhiata più da vicino. Grazie per il consiglio. – Carl

+0

@Carl - notate anche che boost :: mem_fn è più facile da usare rispetto a boost :: bind in questo caso – Manuel

+0

Questa risposta è ortogonale alla domanda, sostituisce il ciclo 'std :: for_each' di' boost :: for_each', ma non dice come usare 'boost :: bind' come argomento per entrambi. Anche così, fornisce una soluzione alternativa. –

Problemi correlati