2011-10-24 7 views
14

Fondamentalmente, io sono un po 'stanco di scrivere:Come posso ridurre il sovraccarico sintattico del controllo dei valori degli iteratori in C++?

std::map<key_t, val_t> the_map; 
... 
auto iterator = the_map.find(...); 
if(iterator != the_map.end()) { // note the "inversed" logic and logically superflous end() call 
    ... 
} 

Ciò che ha reso senso sarebbe:

if(auto x=the_map.find(...)) { 
    ... // x could either be an iterator or maybe something like boost::optional<val_t> 
} 

C'è qualche tecnica che definisce alcune cose aiuto per accorciare la sintassi != container.end() o sono l'unico infastidito da questo?

risposta

7

Si potrebbe scrivere un modello di classe auto_iterator_impl e utilizzarlo attraverso un modello di funzione auto_iterator che restituisce un'istanza di auto_iterator_impl, che può essere implicitamente convertito in true o false:

Un'implementazione di lavoro con funzionalità minime e considerazione:

template<typename C> 
struct auto_iterator_impl 
{ 
    C & c; 
    typename C::iterator it; 
    auto_iterator_impl(C & c, typename C::iterator & it) : c(c), it(it) {} 
    operator bool() const { return it != c.end(); } 
    typename C::iterator operator->() { return it; } 
}; 

template<typename C> 
auto_iterator_impl<C> auto_iterator(C & c, typename C::iterator it) 
{ 
    return auto_iterator_impl<C>(c, it); 
} 

codice di prova:

void test(std::map<int, int> & m, int key) 
{ 
    if (auto x = auto_iterator(m, m.find(key))) 
    { 
     std::cout << "found = " << x->second << std::endl; 
     x->second *= 100; //change it 
    } 
    else 
     std::cout << "not found" << std::endl; 
} 

int main() 
{ 
    std::map<int,int> m; 
    m[1] = 10; 
    m[2] = 20; 
    test(m, 1); 
    test(m, 3); 
    test(m, 2); 

    std::cout <<"print modified values.." <<std::endl; 
    std::cout << m[1] << std::endl; 
    std::cout << m[2] << std::endl; 
} 

uscita:

found = 10 
not found 
found = 20 
print modified values.. 
1000 
2000 

Demo in linea: http://www.ideone.com/MnISh

+0

Il tuo esempio non rende possibile l'uso * * il ritrovamento risultato * *? –

+1

@ Martin: aggiunto con un esempio. – Nawaz

7

Beh, se questo significa che tanto per te, come su di un piccolo involucro:

template <typename Container> 
inline bool find_element(Container const & c, 
         typename Container::const_iterator & it, 
         typename Container::value_type const & val) 
{ 
    return (it = c.find(val)) == c.end(); 
} 

Usage:

std::vector<int> v; 
std::vector<int>::const_iterator it; 

if (find_element(v, it, 12)) { /* use it */ } 
+0

non funziona per le sequenze ..;) – Nim

+0

Nim: Puoi aggiungere specializzazioni che usano 'std :: find (c.begin(), c.end(), val)', forse predicato su un " deve usare generico 'find'" typetrait :-) –

+0

Meh. È necessario pre-dichiarare la variabile iteratore. Brrr! ;-) –

0

È possibile implementare una funzione wrapper per eseguire.210 assegno per voi, o è possibile utilizzare il preprocessore di farlo utilizzando una macro come questa:

#define map_find_if(iterator, container, func) \ 
    auto (iterator) = (func); \ 
    if ((iterator) != (container).end()) 

usare in questo modo:

map_find_if(iter, map, map.find(key)) { 
    // found the key 
} 
// else clause could go here 

Utilizzando una funzione è probabilmente un'implementazione più pulito, però, e più nel "modo C++".

+0

Non usare le macro quando puoi fare diversamente, davvero: x –

1

credo sia la decisione più flessibile per scrivere un involucro come

template<class Iterable> 
bool CheckIterator(const typename Iterable::iterator& iter, const Iterable& collection) 
{ 
    return !(iter == collection.end()); 
} 

e usarlo

map<int,string> m; 
    m[1]="hello"; 
    m[2]="world"; 
    map<int,string>::iterator it = m.find(2); 
    if(CheckIterator(it,m)) 
    { 
     cout<<it->second; 
    } 

può essere utilizzato con altri tipi di contenitori (es vettore di) così

+0

Questa potrebbe essere davvero una bella soluzione low-tech :-) (Anche se lo chiamerei diversamente.) –

1

Che ne dici di questo?

BOOST_FOREACH(auto &x : c.equal_range(...)) { 
    // x is a val_t &, or const if c is const. 
} 

Sto assumendo che se va bene per x per essere boost::optional<val_t>, allora non si fa effettivamente bisogno l'iteratore, solo il valore, quindi un riferimento è OK.

è possibile utilizzare un C++ 11 gamma-based per ciclo invece di BOOST_FOREACH se si utilizza un boost::iterator_range invece di una std::pair (che è quello equal_range rendimenti).

+0

' c.equal_range (t) '? Altrimenti applaudo lo sforzo di indirizzare le persone verso i range :) –

+0

@ Matthieu: buona chiamata, mi sono perso completamente! –

0

Se si desidera inserire il codice in una funzione anonima (sembra che si stia già utilizzando auto, assumendo così C++ 11)?

#include <iostream> 
#include <map> 

template <typename Container, typename ExecF> 
void exec_if(Container const& cont, typename Container::const_iterator it, ExecF f) 
{ 
    if (it != cont.end()) 
    f(); 
} 

int main(void) 
{ 
    std::map<int, int> bar; 
    bar.insert(std::make_pair(1, 1)); 
    bar.insert(std::make_pair(2, 2)); 
    bar.insert(std::make_pair(3, 3)); 

    auto it = bar.find(2); 
    exec_if(bar, it, [&it]() 
    { 
     std::cout << it->first << ' ' << it->second << std::endl; 
    }); 

} 

EDIT: ora penso che sia molto più pulito

Problemi correlati