2012-01-25 10 views
10

in C++ 11 se abbiamo un set<int> S; potremmo dire:range-based per in C++ 11

for (auto i: S) 
    cout << i << endl; 

ma possiamo costringere i essere un iteratore, intendo scrivere un codice che è equivalente a:

for (auto i = S.begin(); i != S.end(); i++) 
    cout << (i != s.begin()) ? " " : "" << *i; 

o potremmo fare qualcosa che possiamo capire l'indice di i nel set (o vettore)?

e un'altra domanda è come potremmo dire che non lo facciamo per tutti gli elementi in S ma per la prima metà di loro o tutti, tranne il primo.

o quando abbiamo un vector<int> V e vogliamo stampare i suoi primi valori n cosa dovremmo fare? So che possiamo creare un nuovo vettore ma ci vuole tempo per copiare un vettore in un nuovo vettore.

+0

Ho chiesto una domanda simillar qui: http://stackoverflow.com/questions/8960403/get-first-n-elements-in-ac-multiset (per multiset invece) – Cristy

+1

Per stampare il primo n valori di un 'vector':' for (auto val: vec | sliced ​​(0, n)) {...} '. Vedi ['sliced', da Boost.Range] (http://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/adaptors/reference/sliced.html). –

+1

Lo scopo del 'for' basato sulla gamma è quello di essere in grado di iterare più facilmente su un intero intervallo. Lo scopo è * non * sostituire completamente non-range 'for'. Ci sono cose per le quali hai bisogno di regolare 'for'; la gamma 'for' è solo zucchero sintattico per un modello di iterazione comune. Non coprirà tutto e non dovrebbe. –

risposta

20

No, sfortunatamente. Vedere ciò che la norma dice:

La gamma-based per dichiarazione per (per la gamma-dichiarazione: espressione) istruzione è equivalente a

{ 
    auto && __range = (expression); 
    for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { 
     for-range-declaration = *__begin; 
     statement 
    } 
} 

dove __range, __begin e __end sono variabili definito per l'esposizione solo

In altre parole, è già itera da begin a end e già dereference l'iteratore, che n mai vedere.

+0

Interessante, ciò significa che __begin e __end devono essere dello stesso tipo. – bcmpinc

+0

@bcmpinc: Ma potrebbero mai essere di tipo diverso? Si può solo scorrere su un array o su un contenitore basato su un modello (beh no, tecnicamente si può iterare su tutto ciò che :: begin() e :: end() hanno sovraccarichi per ... ma si ottiene ciò che intendo), quindi tutto elementi quali __begin e __end sono comunque dello stesso tipo. Potrebbero essere indicatori di classi derivate, sì ... ma anche quello è lo stesso tipo, anche se uno o l'altro puntatore punta a "qualcosa di diverso". – Damon

+1

Con un forward-iterator di filtraggio (che itera su una collezione, ma visualizza solo valori che soddisfano un predicato) è molto più facile per l'iteratore verificare se è finito (deve farlo comunque) piuttosto che confrontarlo con un iteratore finale. Definendo end() per restituire un diverso tipo fittizio, operatore!= può essere implementato in modo tale da controllare solo se l'iteratore è finito. (Recentemente ho implementato questo iteratore.) – bcmpinc

2

No, non è possibile.

for (... : ...) 

si chiama for invece di foreach solo per il motivo di non introdurre una nuova parola chiave. L'intero punto di foreach è una breve sintassi rapida per iterare tutti gli elementi senza preoccuparsi del loro indice. Per tutte le altre situazioni c'è il semplice for che serve al suo scopo in modo abbastanza efficace.

0

L'intervallo for è concepito per casi semplici. Mi aspetterei di essere leggermente utile mentre protoyping qualcosa, ma mi aspetterei che gli usi siano per lo più molto prima che le cose diventino effettivamente un prodotto. Potrebbe essere utile semplificare la vita per i principianti, ma questa è un'area che non posso giudicare (ma ciò che sembra guidare molte delle recenti discussioni in C++).

L'unico approccio un po 'costruttivo potrebbe essere quello di utilizzare un adattatore che fa riferimento all'intervallo sottostante e i cui metodi begin() e end() regolano l'iteratore in modo appropriato. Si noti inoltre che probabilmente si desidera sollevare qualsiasi gestione speciale del primo o dell'ultimo elemento fuori dal ciclo che elabora la maggior parte dei dati. Certo, è solo un altro controllo seguito da un ramo previsto correttamente rispetto a nessun controllo e meno inquinamento delle tabelle di previsione dei rami.

2

Non è possibile in un set. Utilizzare la sintassi tradizionale for o gestire il proprio contatore di indici.

È possibile in un vector o altro contenitore con un layout piatto come std::array o un array di C-stile. Cambialo in usa un riferimento.:

for (auto &i: S) 

Quindi è possibile confrontare l'indirizzo del i con l'indirizzo del s[0] per ottenere l'indice.

+0

Cosa ti impedisce di fare lo stesso con 'std :: set'? 'for (auto & i: S) {if (& i == & * S.begin()) {' – MSalters

+0

@MSalters: Penso che OP chieda l'indice di qualsiasi 'i', ad es. per rilevare se ero nella "prima metà" del set. Hai ragione che puoi provare per il caso speciale dell'indice 0. –

+0

Ho capito che era specificamente il primo, poiché l'intento è di stampare i separatori 'N-1' tra gli elementi' N'. – MSalters

2

Per il caso generale, che avrebbe dovuto utilizzare una variabile indipendente:

int i = 0; 

for (auto x : s) 
    cout << (i++ ? " " : "") << x << endl; 

ci sono, naturalmente, alcuni trucchi per contenitori come vector, ma nessuno di lavoro per ogni contenitore.

Probabilmente starai meglio usando il ciclo normale for per questo scopo.

9

Il principio della gamma for è di iterare su tutto il campo.

Tuttavia è decidere quale è l'intervallo, quindi è possibile operare sulla gamma stessa.

template <typename It> 
class RangeView { 
public: 
    typedef It iterator; 

    RangeView(): _begin(), _end() {} 
    RangeView(iterator begin, iterator end): _begin(begin), _end(end) {} 

    iterator begin() const { return _begin; } 
    iterator end() const { return _end; } 

private: 
    iterator _begin; 
    iterator _end; 
}; 

template <typename C> 
RangeView<typename C::iterator> rangeView(C& c, size_t begin, size_t end) { 
    return RangeView<typename C::iterator>(
      std::next(c.begin(), begin), 
      std::next(c.begin(), end) 
     ); 
} 

template <typename C> 
RangeView<typename C::const_iterator> rangeView(C const& c, size_t begin, size_t end) { 
    return RangeView<typename C::const_iterator>(
      std::next(c.begin(), begin), 
      std::next(c.begin(), end) 
     ); 
} 

Okay, questo Boost.Range seriamente ressemble ...

Ed ora, lo si può usare!

for (auto i: rangeView(set, 1, 10)) { 
    // iterate through the second to the ninth element 
} 
+2

Devo amare i panorami. +1 – Xeo

+0

@Xeo: Vorrei che ne avessimo di più :) Purtroppo alcune viste sono semplici (come questa) ma le viste di "trasformazione" possono diventare davvero complicate. Si adattano bene ai linguaggi funzionali (con immutabilità) ma l'idea di "riferimento" è più difficile del necessario per implementare le trasformazioni (vincoli ai problemi dei provvisori ...). Questo e gli iteratori presto * ingrassano *. –