Domanda

Per un std::map<std::string, std::string> variables, Vorrei fare questo:

BOOST_CHECK_EQUAL(variables["a"], "b");

L'unico problema è, in questo contesto variables è const, così operator[] non funziona :(

Ora, ci sono diverse soluzioni a questo;gettando via il const, utilizzando variables.count("a") ? variables.find("a")->second : std::string() o anche fare una funzione di fasciatura.Nessuno di questi mi sembra di essere così bello come operator[].Cosa devo fare?C'è un modo standard di fare questo (splendidamente)?

Edit: Solo per dichiarare la risposta che nessuno di voi vuole dare:No, non c'è conveniente, bello, modo standard di fare questo in C++.Devo implementare una funzione di supporto.

È stato utile?

Soluzione

template <typename K, typename V>
V get(std::map<K, V> const& map, K const& key)
{
    std::map<K, V>::const_iterator iter(map.find(key));
    return iter != map.end() ? iter->second : V();
}

Una migliore applicazione basata su osservazioni:

template <typename T>
typename T::mapped_type get(T const& map, typename T::key_type const& key)
{
    typename T::const_iterator iter(map.find(key));
    return iter != map.end() ? iter->second : typename T::mapped_type();
}

Altri suggerimenti

Gettando via il const è sbagliato, perché l'operatore[] su mappa<> creare la voce se non è presente il valore predefinito è costruito stringa.Se la mappa è in realtà immutabile archiviazione, quindi si avrà esito negativo.Questo deve essere così, perché l'operatore[] restituisce un non-const riferimento per consentire l'assegnazione.(es.m[1] = 2)

Un veloce libera la funzione di attuare il confronto:

template<typename CONT>
bool check_equal(const CONT& m, const typename CONT::key_type& k,
                    const typename CONT::mapped_type& v)
{
    CONT::const_iterator i(m.find(k));
    if (i == m.end()) return false;
    return i->second == v;
}

Penso zucchero sintattico e di aggiornamento, se penso a qualcosa.

...

Immediate zucchero sintattico coinvolto una funzione che fa una mappa<>::find() e restituisce una classe speciale che avvolge la mappa<>::const_iterator, e poi ha overload dell'operatore==() e l'operatore!=() per consentire il confronto con il mapped tipo.Così si può fare qualcosa di simile:

if (nonmutating_get(m, "key") == "value") { ... }

Io non sono convinto che è molto meglio:

if (check_equal(m, "key", "value")) { ... }

Ed è certamente molto più complesso e quello che sta succedendo è molto meno evidente.

Lo scopo dell'oggetto avvolgendo l'iteratore è smettere di fare default costruito oggetti dati.Se non si cura, quindi basta usare il "get" risposta.

In risposta al commento di ottenere essere preferito ad un confronto, nella speranza di trovare un futuro utilizzo, ho questi commenti:

  • Dire quello che vuoi dire:la chiamata di una funzione chiamata "check_equal" rende chiaro che si sta facendo un confronto di uguaglianza, senza la creazione di oggetti.

  • Mi raccomando solo l'attuazione, la funzionalità di una volta che si dispone di un bisogno.Fare qualcosa prima di allora, è spesso un errore.

  • A seconda della situazione, un costruttore di default potrebbe avere effetti collaterali.Se si confrontano, perché fare di più?

  • SQL argomento:NULL non è equivalente a una stringa vuota.È l'assenza di un tasto dal contenitore e lo stesso come la chiave per essere presenti nel vostro contenitore con un predefinito valore costruito?

Detto tutto questo, un default costruito l'oggetto è equivalente all'utilizzo mappa<>::operator[] su un non-const contenitore.E magari hai un fabbisogno di corrente per una funzione che restituisce un valore predefinito oggetto costruito;So di avere avuto tale requisito in passato.

find è la forma idiomatica.Casting di distanza const è quasi sempre una cattiva idea.È necessario garantire che nessuna operazione di scrittura.Mentre questo può essere ragionevolmente previsto un accesso in lettura su una mappa, le specifiche non dicono nulla su questo.

Se si sapere che il valore esiste ovviamente si può rinunciare al test utilizzando count (che è piuttosto inefficiente, in ogni caso, dal momento che significa che attraversano la mappa due volte.Anche se non so se l'elemento non esiste, che io non uso questo.Utilizzare invece la seguente:

T const& item(map<TKey, T> const& m, TKey const& key, T const& def = T()) {
    map<TKey, T>::const_iterator i = m.find(key);
    return i == m.end() ? def : i->second;
}

/EDIT:Come Chris ha correttamente sottolineato, di default-costruzione di oggetti di tipo T potrebbe essere costoso, soprattutto dal momento che questo è fatto, anche se questo oggetto non è in realtà necessario (perché la voce esiste).Se questo è il caso, non utilizzare il valore predefinito per il def argomento nel caso di cui sopra.

Un interessante a parte, ci sono due modi per fare il tipo di modello scoperta nell'implementazione che è stata accettata (colui che ottiene il valore o predefinita restituisce un oggetto costruito).Uno, si può fare quello che è stato accettato e sono:

template <typename K, typename V>
V get1(const std::map<K, V>& theMap, const K const key)
{
    std::map<K, V>::const_iterator iter(theMap.find(key));
    return iter != theMap.end() ? iter->second : V();
}

oppure è possibile utilizzare il tipo di mappa e ottenere il tipi di fuori di che:

template<typename T>
typename T::mapped_type
get2(const T& theMap, const typename T::key_type& key)
{
    typename T::const_iterator itr = theMap.find(key);
    return itr != theMap.end() ? itr->second : typename T::mapped_type();
}

Il vantaggio di questo è che il tipo di chiave, essendo passati non gioca nell'individuazione del tipo e può essere qualcosa che può essere convertito implicitamente una chiave.Per esempio:

std::map<std::string, int> data;
get1(data, "hey"); // doesn't compile because the key type is ambiguous
get2(data, "hey"); // just fine, a const char* can be converted to a string

Infatti, l'operatore[] è un non-const uno su std::map, dal momento che si inserisce automaticamente una coppia chiave-valore in mappa se non ci fosse.(Oooh effetti collaterali!)

Il modo giusto è utilizzando map::find e, se l'iteratore restituito è valido (!= map.end()), restituendo l' second, come hai dimostrato.

map<int, int> m;
m[1]=5; m[2]=6; // fill in some couples
...
map<int,int>::const_iterator it = m.find( 3 );
if( it != m.end() ) {
    int value = it->second;
    // ... do stuff with value
}

Si potrebbe aggiungere un map::operator[]( const key_type& key ) const in una sottoclasse della std::map che si sta utilizzando, e di affermare la chiave per essere trovato, dopo di che si torna il it->second.

std::map<std::string, std::string>::const_iterator it( m.find("a") );
BOOST_CHECK_EQUAL( 
                     ( it == m.end() ? std::string("") : it->second ), 
                     "b" 
                 );

Che non guarda troppo brutto per me...Io probabilmente non scrivere una funzione per questo.

Di seguito xtofl l'idea di specializzarsi mappa contenitore.Il seguente lavoro bene?

template <typename K,typename V>  
struct Dictionary:public std::map<K,V>  
{  
  const V& operator[] (const K& key) const  
  {  
    std::map<K,V>::const_iterator iter(this->find(key));  
    BOOST_VERIFY(iter!=this->end());  
    return iter->second;  
  }  
};  
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top