2012-06-17 15 views
7

Qual è il modo più elegante di eseguire un ciclo e fermarsi dopo il penultimo elemento (in C++ 11)?Stop agli ultimi iteratori dell'elemento C++

Nota: intendo per gli iteratori bidirezionali; gli iteratori ad accesso casuale sono un caso speciale banale, ovviamente, perché hanno operatori + e -.

std::list<double> x{1,2,3,4,5,6}; 

for (auto iter = x.begin(); iter != x.end(); ++iter) { 
    auto iter2 = iter; 
    ++iter2; 
    if (iter2 == x.end()) break; 
    std::cout << *iter << std::endl; 
} 
+0

Sei sicuro di non volere dire "iteratori di inoltro" anziché "iteratori bidirezionali"? Anche il caso bidirezionale sembra piuttosto banale. –

+0

@LucTouraille 'std :: list' fornisce iteratori bidirezionali, quindi va bene per me-- puoi mostrare la soluzione banale a cui stai pensando? – user

+0

Se 'while (iter! = X.end() - 1)' è banale per te, allora 'while (iter! = --x.end())' dovrebbe essere abbastanza semplice, no? –

risposta

14

utilizzare la funzione std::prev:

std::list<double> x{1,2,3,4,5,6}; 

for (auto iter = x.begin(); iter != std::prev(x.end()); ++iter) { 
    std::cout << *iter << std::endl; 
} 
+0

Non posso dire di vedere qualcosa di particolarmente avvincente su 'std :: prev' qui. Più digitazione è meglio? ; -] – ildjarn

+0

@ildjarn Beh, mi sono un po 'confuso da "penultimo elemento" e ho pensato che sarebbe 'std :: prev (x.end(), 2)': S –

+0

Funziona se 'x. begin() == x.end() '? –

5

In C++ 03 sarebbe stato:

for (std::list<double>::iterator it = x.begin(), it_last = --x.end(); 
    it != it_last; ++it) 
{ 
    std::cout << *it << '\n'; 
} 

In C++ 11, non c'è nulla di fondamentalmente diverso, è solo meno verbose ..:

for (auto it = begin(x), it_last = --end(x); it != it_last; ++it) 
{ 
    std::cout << *it << '\n'; 
} 
+0

'x.end() -' dovrebbe essere '--x.end()'. –

+0

@Luc: Sì, stavo pensando che '--end (x)' non avrebbe funzionato per gli iteratori in generale (cioè se l'iteratore fosse in realtà solo un puntatore), ma poi ho capito che 'end (x) - -Non lo farei neanche. :-P – ildjarn

+0

In pratica questo può essere più tipografico della risposta di R. Martinho, perché funziona per qualsiasi iteratore bidirezionale, mentre questo no. Quindi, se usato in un algoritmo che prende un iteratore, ciò richiederebbe specializzazione/sovraccarico per i puntatori o la distribuzione dei tag per gli iteratori di accesso casuale in generale. Ma se 'std :: list' è inteso come l'intera domanda, piuttosto che un semplice esempio, allora lo farà. –

1

Un lieve miglioramento R. Martinho Fernandes risposta:

Utilizzare la funzione std::prev:

std::list<double> x{1,2,3,4,5,6}; 

for (auto iter = x.begin(), end=std::prev(x.end()); iter != end; ++iter) { 
    std::cout << *iter << std::endl; 
} 

Questo calcola solamente: std::prev(x.end()) volta.

+3

Ad essere sincero, non ho nemmeno pensato a questo miglioramento. Se dovessi migliorare quel codice, andrei direttamente su 'for (auto && x: drop_last (x))': stesse caratteristiche di performance, nessuna leggibilità persa (al contrario). –

+0

Qualcuno sa se ci sarà un guadagno in termini di prestazioni con l'ottimizzazione del compilatore (ad esempio '-O3' w/gcc)? – user

+0

@ R.MartinhoFernandes +1 per 'drop_last', anche se la mia applicazione è migliore con' std :: prev (...) 'per il loop-range qui (in ogni iterazione del ciclo ho bisogno di usare' * iter' e (quello che sto indovinando è) '* std :: next (iter)'). – user