size_t c++ c++11 for-loop

size_t - c++11 download



¿Puede C++ 11 basado en rango para hacer/verificar operaciones/condiciones adicionales? (6)

Aquí hay algo que puede hacer # 2

#include <iterator> #include <utility> #include <type_traits> #include <cstddef> template<typename Range> class RangeBasedAdaptor { Range& range; public: RangeBasedAdaptor(Range& r) : range(r) {} struct iterator; typedef typename std::remove_reference<decltype(*std::begin(range))>::type mapped_type; typedef decltype(std::begin(range)) underlying_iterator; struct value_type { std::size_t index() const { return idx; } mapped_type& value() { return *ui; } const mapped_type& value() const { return *ui; } private: std::size_t idx; underlying_iterator ui; friend struct iterator; }; struct iterator { iterator(); iterator& operator++() { ++val.ui; ++val.idx; return *this; } value_type& operator*() { return val; } bool operator!=(iterator other) { return val.ui != other.val.ui; } private: iterator( underlying_iterator ui, std::size_t idx ) { val.idx=idx; val.ui=ui; } value_type val; friend class RangeBasedAdaptor; }; iterator begin() { return iterator{ std::begin(range), 0 }; } iterator end() { return iterator{ std::end(range), (std::size_t)-1 }; } }; template<typename Range> auto indexed(Range& r) -> RangeBasedAdaptor<Range> { return {r}; } // ------------------------------------------------------------------------------------- #include <iostream> #include <vector> #include <list> int main() { std::vector<int> foo = { 1,2,3,4,5,6 }; for( auto& val : indexed(foo) ) { val.value() += 3; std::cout << val.index() << " : " << val.value() << std::endl; } const std::list<float> foo2 = { 1.1f, 2.2f, 3.3f }; for( auto& val : indexed(foo2) ) { std::cout << val.index() << " : " << val.value() << std::endl; } }

Solo está diseñado con bucles basados ​​en rangos, de ahí el iterador mínimo.

Estoy descubriendo el loop basado en C ++ 11 y ya me encanta. Le hace ahorrar mucho tiempo cuando codifica.

Sin embargo, estoy acostumbrado a escribir algunos bucles con sentencias / condiciones adicionales y me pregunto si esto se puede lograr al usar el bucle basado en el rango C ++ 11:

1. Incremento adicional

std::vector<int> v = { 1, 2, 3, 4, 5 }; size_t index = 0; for ( std::vector<int>::const_iterator iter = v.begin(); iter != v.end(); ++iter, ++index ) { std::cout << "v at index " << index << " is " << *iter; }

Podría convertirse:

size_t index = 0; for ( int val : v ) { std::cout << "v at index " << index << " is " << *iter; ++index; }

Sin embargo, incrementar el index en el bucle for es mejor porque está garantizado (incrementado incluso si el bucle tiene instrucciones continue por ejemplo)

¿Hay alguna manera de mover el ++index dentro de la instrucción for ?

2. Obtenga el índice de iteración dinámicamente

std::vector<int> v = { 1, 2, 3, 4, 5 }; for ( std::vector<int>::const_iterator iter = v.begin(); iter != v.end(); ++iter ) { std::cout << "v at index " << ( iter - v.begin() ) << " is " << *iter; }

¿Se puede lograr algo similar con el ciclo basado en el rango de C ++ 11? ¿Hay alguna forma de saber cuántas iteraciones se realizaron hasta ahora?

3. condición de salida extra

A menudo uso esto en el código donde el break está prohibido como una guía de codificación:

std::vector<int> v = { 1, 2, 3, 4, 5 }; bool continueLoop = true; for ( std::vector<int>::const_iterator iter = v.begin(); iter != v.end() && continueLoop; ++iter ) { std::cout << "v value is " << *iter; if ( *iter == 4 ) continueLoop = false; }

¿Se puede lograr algo similar con el ciclo basado en el rango de C ++ 11 (la excepción de ruptura sin usar un descanso)?


Desafortunadamente, no puedes poner el incremento en el rango basado en el ciclo. Sin embargo, en su caso específico, como std::vector almacena sus elementos contundentemente en la memoria, puede simular la opción 2 volviendo a los punteros (gracias a @MM y @ Jarod42 para correcciones y mejoras):

for ( const int& val : v ) { std::cout << "v at index " << &val-v.data() << " is " << val; }

más genérico:

for ( const auto& val : v ) { std::cout << "v at index " << std::addressof(val)-v.data() << " is " << val; }

La otra cosa que puede hacer es escribir una clase index_range , que representa una colección de índices sobre los cuales puede iterar en su rango basado en bucle:

struct index_range_it { size_t idx; size_t operator*(){ return idx; } index_range_it& operator++() { idx++; return (*this); } }; bool operator!=(index_range_it l,index_range_it r) { return l.idx != r.idx; } struct index_range { size_t size; index_range_it end(){return index_range_it{size};} index_range_it begin(){return index_range_it{0};} }; int main() { for (auto i: index_range{v.size()}){ std::cout << "v at index " << i << " is " << v[i]; } }

Una completa implementación de esta idea se puede encontrar, por ejemplo, here

Tal rango también puede estar compuesto por algo, donde el iterador devuelve un objeto proxy que contiene el índice, así como una referencia al objeto actual y con el enlace estructurado de c ++ 17 que sería aún más conveniente de usar.


Eche un vistazo a range-v3 y cppitertools .

cppitertools proporciona una enumerate muy conveniente:

std::vector<int> v = { 1, 2, 3, 4, 5 }; for (auto&& e : enumerate(v)) { std::cout << "v at index " << e.index << " is " << e.element; }

Range-v3 desafortunadamente no tiene enumerate, lo que me entristece, pero puede componer el suyo usando view::ints y view::zip * . Range-v3 tiene la gran ventaja de que es la base para los rangos propuestos en la biblioteca estándar. La composición de rango permite construir abstracciones limpias.

En cuanto a su último ejemplo, yo diría que debe evitar un ciclo completo si necesita reducir la complejidad. En su lugar, utilice un algoritmo apropiado como std::find_if , std::any_of que coincida con su tarea sin tener que expresar el flujo de control.


En los lenguajes de computadora, tradicionalmente un bucle "for" es un bucle con condiciones de bucle especificadas por el lenguaje. Si el programador desea especificar sus propias condiciones de bucle, utilizan un ciclo "while". Desde esta perspectiva, los bucles for basados ​​en rangos de C ++ son la primera vez que el lenguaje realmente tiene una verdadera construcción de bucle "para". Por lo tanto, a un programador de C ++ le puede llevar un poco pensar en el hecho de que si no pueden lidiar con las condiciones de bucle generadas por el compilador, deberían usar una construcción diferente.

Dicho esto, dado que los iteradores pueden ser objetos personalizados, puede hacer lo que desee con un bucle for basado en rango escribiéndose un iterador personalizado. En el pasado, normalmente he encontrado que este esfuerzo no vale la pena el código adicional, a menos que vayas a reutilizar ese iterador varias veces.

1. Incremento adicional

Sin embargo, incrementar el índice en el bucle for es mejor porque está garantizado (incrementado incluso si el bucle tiene instrucciones continue por ejemplo)

¿Hay alguna manera de mover el índice ++ dentro de la instrucción for?

Sí, con un iterador personalizado. Sin embargo, eso es mucho trabajo. Esto es facil:

for (auto element : container) { ++index; }

Aquí también sabemos que está garantizado para aumentar, porque se coloca en la parte superior antes de cualquier posible ruptura o continuar las declaraciones.

  1. Obtenga el índice de iteración dinámicamente

¿Se puede lograr algo similar con el ciclo basado en el rango de C ++ 11? ¿Hay alguna forma de saber cuántas iteraciones se realizaron hasta ahora?

Una vez más, esto podría hacerse con un iterador personalizado, pero casi seguro que no vale la pena. Tuve que hacer esto la semana pasada, y la solución se parecía mucho al código en el n. ° 1 anterior.

  1. Condición de salida extra

A menudo uso esto en el código donde el break está prohibido como una guía de codificación:

Esto nunca debería estar en una pauta de codificación. Está completamente equivocado. No estoy argumentando para que rompa sus pautas. Pero estoy argumentando que cualquiera que lea esto nunca volverá a poner tal cosa en un documento de directrices de codificación.

Existe una regla empírica común para una buena codificación estructurada de que cualquier bloque de código solo debe tener un punto de salida (también conocido como: goto se considera dañino ). Sin embargo, un ciclo con dos instrucciones de salida todavía tiene un solo punto de salida . Ambas salidas devuelven el control al mismo punto fuera del bucle.

Más prácticamente, hay muchos tipos de bucles que tienen que ser mucho más complicados (por ej., Más difíciles de entender y seguir funcionando correctamente) si no puede poner su prueba de salida en el medio de ellos. Si una directriz te obliga rutinariamente a escribir más código obtuso, es una mala guía.

Nuevamente, podría solucionar esto con un iterador personalizado. En este caso, yo diría que puede ser el camino a seguir. Claro, es mucho más código de lo que vale solo para evitar su estúpida guía de codificación. Pero esa es la falla de la guía, no la tuya.


No escribiré código que reemplace una declaración de break perfectamente buena.

Obtener el índice para un vector (que es cuando es útil) es fácil: iterador sobre auto& x:v y luego restar std::addressof(x)-v.data() .

Que deja # 1.

template<class It, class Operation> struct iterator_with_extra_increment_t { using self=iterator_with_extra_increment_t; It it; Operation& op; void operator++(){ ++it; op(); } auto operator*()->decltype(*std::declval<It&>()) { return *it; } friend bool operator!=(self const& lhs, self const& rhs){ return lhs.it != rhs.it; } friend bool operator==(self const& lhs, self const& rhs){ return lhs.it == rhs.it; } }; template<class It, class Operation> iterator_with_extra_increment_t<It, Operation> iterator_with_extra_increment( It it, Operation& operation ) { return {std::move(it), operation}; } template<class Range, class Modify> struct iterate_modified_t { Range r; Modify m; auto begin() { using std::begin; return m(begin(r)); } auto end() { using std::end; return m(end(r)); } }; template<class Range, class Modify> iterate_modified_t<Range, std::decay_t<Modify>> iterate_modified( Range&& r, Modify&& m) { return {std::forward<Range>(r), std::forward<Modify>(m)}; } template<class Range, class Op> auto also_on_inc( Range&& r, Op&& op ) { auto modify = [op = std::forward<Op>(op)](auto&& it) { return iterator_with_extra_increment(decltype(it)(it), op); }; return iterate_modified( std::forward<Range>(r), std::move(modify) ); }

ahora tenemos also_on_inc :

std::vector<int> a = {1,2,3,4,5}; std::size_t count = 0; for (int x : also_on_inc(a, [&]{++count;}) ) { std::cout << count << "->" << x << ''/n''; }

ejemplo en vivo

Parte del código anterior es C ++ 14 porque soy demasiado flojo para escribir las cláusulas ->decltype .

Podemos mejorar esa sintaxis con el abuso del operador a algo como:

std::vector<int> a = {1,2,3,4,5}; std::size_t count = 0; for (int x : a *also_on_inc* [&]{++count;} ) { std::cout << count << "->" << x << ''/n''; }

si estamos locos, lo que nos permite hacer

std::vector<int> a = {1,2,3,4,5}; std::size_t count = 0; for (int x : a *also_on_inc* [&]{++count;} *also_on_inc* [&]{std::cout << count << ''/n'';} ) { std::cout << count << "->" << x << ''/n''; }

encadenamiento más fácil de tales cláusulas.


Para un contenedor general, no puede obtener el índice ni el iterador de un bucle de rango. En su lugar, debe mantener una variable separada o volver al ciclo del iterador.

El aspecto del iterador se puede escribir de forma un poco más simple desde C ++ 11:

for( auto iter = begin(v); iter != end(v); ++iter )

Para el caso específico de un vector, puede hacer:

for ( auto& val : v ) { cout << "Index is " << (&val - &v[0]) << ''/n''; }

que funciona porque los vectores usan almacenamiento contiguo.