for c++ c++11 foreach

c++ - ¿Cómo puedo verificar si estoy en el último elemento al iterar usando la sintaxis de foreach



for en javascript (6)

Esta pregunta ya tiene una respuesta aquí:

Por ejemplo:

for( auto &iter: item_vector ) { if(not_on_the_last_element) printf(", "); }

o

for( auto &iter: skill_level_map ) { if(not_on_the_last_element) printf(", "); }


No hay un buen método. Pero si tenemos fácil acceso al último elemento del contenedor ...

std::vector<int> item_vector = ...; for (auto & elem : item_vector) { ... if (&elem != &item_vector.back()) printf(", "); }


No puedes realmente Ese es el punto de range-for, es que no necesitas iteradores. Pero puedes cambiar tu lógica sobre cómo imprimir tu coma para imprimirla si no es la primera:

bool first = true; for (auto& elem : item_vector) { if (!first) printf(", "); // print elem first = false; }

Si esa es la intención del bucle de todos modos. O puede comparar las direcciones:

for (auto& elem : item_vector) { if (&elem != &item_vector.back()) printf(", "); // ... }


El rango basado en el ciclo se realiza para iterar en todo el rango. Si no quieres eso, ¿por qué no haces un ciclo regular?

auto end = vector.end() - 1; for (auto iter = vector.begin(); iter != end; ++iter) { // do your thing printf(", "); } // do your thing for the last element

Si no quiere repetir el código dos veces para "hacer lo suyo", como lo haría, cree una lambda que lo llame y lo llame:

auto end = vector.end() - 1; // create lambda for (auto iter = vector.begin(); iter != end; ++iter) { lambda(*iter); printf(", "); } lambda(vector.back());


Este tipo de bucles se escribe mejor usando la construcción " Loop and a Half ":

#include <iostream> #include <vector> int main() { auto somelist = std::vector<int>{1,2,3,4,5,6,6,7,8,9,6}; auto first = begin(somelist), last = end(somelist); if (first != last) { // initial check while (true) { std::cout << *first++; if (first == last) break; // check in the middle std::cout << ", "; } } }

Ejemplo en vivo que imprime

1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 6

es decir, sin un separador al final del último elemento.

El chequeo en el medio es lo que lo hace diferente de do-while (chequear por adelantado) o por_each / range-based para (verificar al final). Intentar forzar un bucle regular para estos bucles introducirá ramas condicional adicionales o lógica de programa duplicada.


Esto es como un patrón de estado.

#include <iostream> #include <vector> #include <functional> int main() { std::vector<int> example = {1,2,3,4,5}; typedef std::function<void(void)> Call; Call f = [](){}; Call printComma = [](){ std::cout << ", "; }; Call noPrint = [&](){ f=printComma; }; f = noPrint; for(const auto& e:example){ f(); std::cout << e; } return 0; } Output: 1, 2, 3, 4, 5

La primera vez a través de f apunta a noPrint que solo sirve para hacer que f apunte para printComma , por lo que las comas solo se imprimen antes del segundo y siguientes elementos.


Guarde este código de manera segura en un archivo de encabezado en su pequeña bolsa de utilidades:

namespace detail { template<class Iter> struct sequence_emitter { sequence_emitter(Iter first, Iter last, std::string sep) : _first(std::move(first)) , _last(std::move(last)) , _sep(std::move(sep)) {} void write(std::ostream& os) const { bool first_element = true; for (auto current = _first ; current != _last ; ++current, first_element = false) { if (!first_element) os << _sep; os << *current; } } private: Iter _first, _last; std::string _sep; }; template<class Iter> std::ostream& operator<<(std::ostream& os, const sequence_emitter<Iter>& se) { se.write(os); return os; } } template<class Iter> detail::sequence_emitter<Iter> emit_sequence(Iter first, Iter last, std::string separator = ", ") { return detail::sequence_emitter<Iter>(std::move(first), std::move(last), std::move(separator)); }

entonces puedes emitir cualquier rango de cualquier contenedor sin un separador final como este:

vector<int> x { 0, 1, 2, 3, 4, 5 }; cout << emit_sequence(begin(x), end(x)) << endl; set<string> s { "foo", "bar", "baz" }; cout << emit_sequence(begin(s), end(s), " comes before ") << endl;

Rendimiento esperado:

0, 1, 2, 3, 4, 5 bar comes before baz comes before foo