2012-06-17 9 views
7

¿Cuál es la forma más elegante de realizar un ciclo y parar después del penúltimo elemento (en C++ 11)?Detener en los iteradores del último elemento C++

Nota: me refiero a los iteradores bidireccionales; los iteradores de acceso aleatorio son un caso especial trivial, por supuesto, porque tienen operadores + y -.

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

¿Estás seguro de que no querías decir "iteradores directos" en lugar de "iteradores bidireccionales"? El caso bidireccional parece bastante trivial también. –

+0

@LucTouraille 'std :: list' proporciona iteradores bidireccionales, así que me parece bien. ¿Puedes mostrar la solución trivial en la que estás pensando? – user

+0

Si 'while (iter! = X.end() - 1)' es trivial para usted, entonces 'while (iter! = --x.end())' también debería ser bastante directo, ¿no? –

Respuesta

14

utilizar la función 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

No puedo decir que veo algo particularmente convincente sobre 'std :: prev' aquí. ¿Más mecanografía es mejor? ; -] – ildjarn

+0

@ildjarn Bueno, me confundí un poco con el "penúltimo elemento" y pensé que sería 'std :: prev (x.end(), 2)': S –

+0

Funciona si 'x. begin() == x.end() '? –

5

En C++ 03 habría sido:

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

En C++ 11, no hay nada fundamentalmente diferente, es un poco menos verboso ..:

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

'x.end() -' debe ser '--x.end()'. –

+0

@Luc: Sí, estaba pensando que '--end (x)' no funcionaría para los iteradores en general (es decir, si el iterador en realidad era solo un puntero), pero luego me di cuenta de que 'end (x) - -'tampoco lo haría. :-P – ildjarn

+0

En la práctica, esto puede ser más tipeo que la respuesta de R. Martinho, porque eso funciona para cualquier iterador bidireccional, mientras que no. Entonces, si se usa en un algoritmo que toma un iterador, esto necesitaría especialización/sobrecarga para punteros o despacho de etiquetas para iteradores de acceso aleatorio en general. Pero si 'std :: list' pretende ser la pregunta completa, en lugar de solo un ejemplo, entonces esto servirá. –

1

una ligera mejora en la respuesta R. Martinho Fernandes:

utilizar la función 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; 
} 

Esto sólo calcula: std::prev(x.end()) vez.

+3

Para ser honesto, ni siquiera pensé en esta mejora. Si tuviera que mejorar ese código, iría directamente a 'for (auto && x: drop_last (x))': las mismas características de rendimiento, sin legibilidad perdida (por el contrario). –

+0

¿Alguien sabe si habrá alguna ganancia de rendimiento con la optimización del compilador (es decir, '-O3' w/gcc)? – user

+0

@ R.MartinhoFernandes +1 para 'drop_last', aunque mi aplicación es mejor con' std :: prev (...) 'en el loop-loop aquí (en cada iteración de bucle necesito usar' * iter' y (lo que supongo es) '* std :: next (iter)'). – user

Cuestiones relacionadas