2013-03-20 10 views
11

I nuovi loop basati su range migliorano davvero la leggibilità e sono davvero facili da usare. Tuttavia, considerare quanto segue:C++ 11 range-based per e mappa: leggibilità

map<Foo,Bar> FooAndAssociatedBars; 

for (auto& FooAndAssociatedBar : FooAndAssociatedBars) { 
    FooAndAssociatedBar.first.doSth(); 
    FooAndAssociatedBar.second.doSomeOtherThing(); 
} 

Può essere un dettaglio, ma trovo che sarebbe stato più leggibile se avrei potuto fare qualcosa di simile:

for ((auto& foo, auto& bar) : FooAndAssociatedBars) { 
    foo.doSth(); 
    bar.doSomeOtherThing(); 
} 

Sai una sintassi equivalente?

EDIT: Buone notizie: C++ 17 ha una proposta che adresses questo problema, chiamato binding strutturati (vedi 1). In C++ 17, si dovrebbe essere in grado di scrivere:

tuple<T1,T2,T3> f(/*...*/) { 
    /*...*/ 
    return {a,b,c}; 
} 
auto [x,y,z] = f(); // x has type T1, y has type T2, z has type T3 

che risolve questo problema leggibilità

+0

In che modo il compilatore potrebbe indovinare a cosa dovevano riferirsi 'Foo' e' Bar'? –

+0

@PeteBecker Beh, foo è solo un modo conveniente per dire "name foo la parte sinistra della std :: pair". In termini assoluti, è possibile farlo in fase di compilazione: questa è solo una comodità notazionale. Mi chiedevo se fosse possibile ottenere tale effetto tramite sovraccarico o qualcosa del genere –

+0

Sì, è certamente possibile richiedere al compilatore di conoscere 'std :: pair' o cercare qualsiasi struttura con elementi chiamati' first' e ' secondo'; è piuttosto specializzato e probabilmente non appropriato per la standardizzazione. La prossima richiesta sarebbe per tutti gli elementi di una 'tupla' ... –

risposta

10

Non esiste una cosa come si desidera. Il più vicino è quello di dichiarare le variabili all'interno del ciclo:

for (auto& FooAndAssociatedBar : FooAndAssociatedBars) { 
    auto& foo = FooAndAssociatedBar.first; 
    auto& bar = FooAndAssociatedBar.second; 

    // ... 
} 
0

E, naturalmente, si ha sempre la possibilità di utilizzare lambda.

std::map<int, const char*> m { { 4, "hello" }, { 11, "c++" } }; 
convenient_for_each(m, [](int a, const char* b) { 
    std::cout << b << a << std::endl; 
    }); 
convenient_for_each(m, [](std::pair<int, const char> p) { 
    std::cout << p.first << p.second << std::endl; 
    }); 

O avvolto come macro (non raccomandato)

FOREACH((int a, const char* b), m, std::cout << a << b << std::endl); 
FOREACH((std::pair<int, const char*> p), m, std::cout << p.first << p.second << std::endl); 

(Hackish sample implementation at LWS)

Auto non funziona, però, sto ancora aspettando lambda polimorfico. Il mio approccio è teoricamente in grado di gestire anche le tuple.

1

Non è una buona idea. Prima o poi, si vorrebbe lo stesso per un std::tuple, e il compilatore dovrebbe essere in grado di utilizzare std::get<> su tuple automaticamente. A mio parere, il tuo approccio ti sta solo piacendo al momento, e troverai dei problemi con questo approccio (supponi che sia implementato in questo modo).

Il comitato standard ha progettato for-loop basato sulla gamma con profonda considerazione. È molto meglio del ciclo foreach in altre lingue, ed è molto più breve. Accoppialo con auto& e il gioco è fatto!

+1

" Non è una buona idea. Prima o poi, si vorrebbe lo stesso per una tupla std :: e il compilatore dovrebbe essere in grado di usare automaticamente std :: get <> sulla tupla. " E il problema con questo è? – Cubic

+0

Ulteriori richieste dal compilatore. – Ajay

+0

In che modo? Il modo in cui lo vedo qualsiasi caratteristica linguistica di questo tipo sarebbe definito per chiamare 'std :: get' (molto probabilmente' get' più probabile) e quindi supportare 'std :: pair' e' std :: tuple' e qualsiasi utente definito ' tuple 'come contenitori automaticamente. – Grizzly