2012-01-25 13 views
10

en C++ 11 si tenemos un set<int> S; podríamos decir:basado en rango para en C++ 11

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

pero podemos forzar i ser un iterador, me refiero a escribir un código que es equivalente a:

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

o podríamos hacer algo que podemos entender el índice de i en el conjunto (o vector)?

y otra pregunta es ¿cómo podemos decir que no haga esto para todos los elementos en S pero para la primera mitad de ellos o todos ellos, excepto el primero.

o cuando tenemos un vector<int> V, y queremos imprimir sus primeros valores n ¿qué debemos hacer? Sé que podemos crear un nuevo vector, pero lleva tiempo copiar un vector a un nuevo vector.

+0

me han preguntado una pregunta simillar aquí: http://stackoverflow.com/questions/8960403/get-first-n-elements-in-ac-multiset (por multiset lugar) – Cristy

+1

Para imprimir los primeros n valores de un 'vector':' for (auto val: vec | sliced ​​(0, n)) {...} '. Ver ['sliced', from Boost.Range] (http://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/adaptors/reference/sliced.html). –

+1

El propósito del 'for' basado en rango es poder iterar más fácilmente en un rango completo. El objetivo es * no * reemplazar por completo el 'rango' no incluido en el rango. Hay cosas para las cuales necesitas un 'for' regular; el rango 'for' es solo azúcar sintáctico para un patrón de iteración común. No cubrirá todo y no se supone que lo haga. –

Respuesta

20

No, desafortunadamente. Ver lo que la norma dice:

La sentencia for para (para-gama-declaración: expresión) a base de gama comunicado es equivalente a

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

donde __range, __begin y __end son variables definido para la exposición única

En otras palabras, ya se itera de begin a end y ya elimina referencia el iterador, que n alguna vez llegue a ver

+0

Interesante, eso significa que __begin y __end deben ser del mismo tipo. – bcmpinc

+0

@bcmpinc: ¿Pero podrían alguna vez ser de otro tipo? Solo puede iterar sobre una matriz o sobre un contenedor con plantillas (bueno, no, técnicamente puede iterar sobre cualquier cosa que :: begin() y :: end() tenga sobrecargas para ... pero entiendo lo que quiero decir), entonces todo los elementos que incluyen __begin y __end son del mismo tipo de todos modos. Podrían ser indicadores de clases derivadas, sí ... pero incluso eso es del mismo tipo, incluso si uno u otro apunta a "algo diferente". – Damon

+1

Con un iterador de filtrado hacia adelante (que itera sobre una colección, pero solo visita valores que satisfacen un predicado) es mucho más fácil para el iterador verificar si está terminado (tiene que hacerlo de todos modos) que compararlo con un iterador final. Al definir end() para devolver un tipo de dummy diferente, ¡operador!= puede implementarse de modo que solo verifique si el iterador ha finalizado. (Recientemente implementé dicho iterador.) – bcmpinc

2

No, no puedes.

for (... : ...) 

se llama for en lugar de foreach sólo por la razón de no introducir una nueva palabra clave. El punto completo de foreach es una sintaxis corta rápida para iterar todos los elementos sin tener en cuenta su índice. Para todas las demás situaciones, es simple for, que cumple su función con bastante eficacia.

0

La base for está diseñada para casos simples. Esperaría algo útil al protoyping algo, pero esperaría que los usos de la misma se hayan ido mucho antes de que las cosas se conviertan en un producto. Posiblemente sea útil para facilitar la vida de los principiantes, pero este es un área que no puedo juzgar (pero que parece impulsar muchas de las discusiones recientes de C++).

El único enfoque un tanto constructivo podría ser usar un adaptador que haga referencia al rango subyacente y cuyos métodos begin() y end() ajusten el iterador de forma adecuada. También tenga en cuenta que probablemente desee elevar cualquier manipulación especial del primer o último elemento fuera del ciclo procesando la mayor parte de los datos. Claro, es solo otro chequeo seguido de una rama correctamente predicha vs. no chequeo y menos contaminación de las tablas de predicción de ramas.

2

No se puede en un set. Use la sintaxis tradicional for o mantenga su propio contador de índice.

Puede en un recipiente vector o de otro tipo con una disposición plana como std::array o una matriz C-estilo. Cámbielo a use una referencia.:

for (auto &i: S) 

A continuación, se puede comparar la dirección de i con la dirección de s[0] para obtener el índice.

+0

¿Qué te impide hacer lo mismo con un 'std :: set'? 'for (auto & i: S) {if (& i == & * S.begin()) {' – MSalters

+0

@MSalters: Creo que OP está pidiendo el índice de cualquier 'i', p. para detectar si estaba en la "primera mitad" del conjunto. Tiene razón en que puede probar el caso especial del índice 0. –

+0

Entendí que era específicamente el primero, ya que la intención es imprimir separadores 'N-1' entre elementos' N'. – MSalters

2

Para el caso general, que tendría que utilizar una variable independiente:

int i = 0; 

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

Hay, por supuesto, trucos para ciertos envases como vector, pero ninguno trabaja para cada contenedor.

Probablemente sea mejor utilizar el loop for normal para este propósito.

9

El principio de la gama for es iterar en todo el rango.

Sin embargo, usted decide cuál es el rango, por lo tanto, puede operar en la misma gama.

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) 
     ); 
} 

Vale, esto Boost.Range serio ressemble ...

Y ahora, vamos a usarlo!

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

Tengo que amar las vistas. +1 – Xeo

+0

@Xeo: Desearía tener más de ellos :) Lamentablemente, algunas vistas son simples (como esta) pero las vistas de "transformación" pueden ser realmente complicadas. Se adaptan bien a los lenguajes funcionales (con inmutabilidad) pero la idea de "referencia" es más difícil de lo necesario para implementar en las transformaciones (vinculante para problemas temporales ...). Eso y los iteradores pronto obtienen * grasa *. –

Cuestiones relacionadas