En C ++ 11 puede usar un rango basado en for
, que actúa como el foreach
de otros lenguajes. Funciona incluso con matrices C simples:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
¿Cómo sabe cuándo detenerse? ¿Solo funciona con matrices estáticas que se han declarado en el mismo ámbito en el que for
se utiliza? ¿Cómo usarías esto for
con matrices dinámicas?
for
. Pero en el momento en que esa matriz se convierte en un puntero, la información de tamaño se pierde.numbers
essizeof(numbers)/sizeof(int)
, por ejemplo.Respuestas:
Funciona para cualquier expresión cuyo tipo sea una matriz. Por ejemplo:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}}; for(int &n : *arraypointer) n *= 2; delete [] arraypointer;
Para una explicación más detallada, si el tipo de expresión pasada a la derecha de
:
es un tipo de matriz, entonces el bucle itera deptr
aptr + size
(ptr
apuntando al primer elemento de la matriz, quesize
es el recuento de elementos de la matriz).Esto contrasta con los tipos definidos por el usuario, que funcionan mirando hacia arriba
begin
yend
como miembros si pasa un objeto de clase o (si no hay miembros llamados de esa manera) funciones no miembros. Esas funciones producirán los iteradores de inicio y final (apuntando directamente después del último elemento y el comienzo de la secuencia, respectivamente).Esta pregunta aclara por qué existe esa diferencia.
fuente
begin
`end. It just happens that
std :: begin`std::end
use las funciones miembro, y se usará si no hay una coincidencia mejor disponible.Creo que la parte más importante de esta pregunta es cómo C ++ sabe cuál es el tamaño de una matriz (al menos quería saberlo cuando encontré esta pregunta).
C ++ conoce el tamaño de una matriz porque es parte de la definición de la matriz, es el tipo de variable. Un compilador debe conocer el tipo.
Dado que C ++ 11
std::extent
se puede utilizar para obtener el tamaño de una matriz:int size1{ std::extent< char[5] >::value }; std::cout << "Array size: " << size1 << std::endl;
Por supuesto, esto no tiene mucho sentido, porque debe proporcionar explícitamente el tamaño en la primera línea, que luego obtiene en la segunda línea. Pero también puedes usar
decltype
y luego se vuelve más interesante:char v[] { 'A', 'B', 'C', 'D' }; int size2{ std::extent< decltype(v) >::value }; std::cout << "Array size: " << size2 << std::endl;
fuente
Según el último borrador de trabajo de C ++ (n3376), la declaración de rango es equivalente a lo siguiente:
{ auto && __range = range-init; for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { for-range-declaration = *__begin; statement } }
Por lo tanto, sabe cómo detenerse de la misma manera que lo hace un
for
bucle regular que usa iteradores.Creo que puede estar buscando algo como lo siguiente para proporcionar una forma de usar la sintaxis anterior con matrices que constan solo de un puntero y tamaño (matrices dinámicas):
template <typename T> class Range { public: Range(T* collection, size_t size) : mCollection(collection), mSize(size) { } T* begin() { return &mCollection[0]; } T* end () { return &mCollection[mSize]; } private: T* mCollection; size_t mSize; };
Esta plantilla de clase se puede usar para crear un rango, sobre el cual puede iterar usando la nueva sintaxis de rango . Estoy usando esto para ejecutar todos los objetos de animación en una escena que se importa usando una biblioteca que solo devuelve un puntero a una matriz y un tamaño como valores separados.
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) ) { // Do something with each pAnimation instance here }
Esta sintaxis es, en mi opinión, mucho más clara de lo que obtendría usando
std::for_each
unfor
bucle simple .fuente
Sabe cuándo detenerse porque conoce los límites de las matrices estáticas.
No estoy seguro de qué quiere decir con "matrices dinámicas", en cualquier caso, si no itera sobre matrices estáticas, informalmente, el compilador busca los nombres
begin
yend
en el alcance de la clase del objeto sobre el que itera, o busca parabegin(range)
yend(range)
usar la búsqueda dependiente de argumentos y los usa como iteradores.Para obtener más información, en el estándar C ++ 11 (o borrador público del mismo), "6.5.4 La
for
declaración basada en rangos ", pág.145fuente
new[]
. En ese caso, solo tiene un puntero sin indicación del tamaño, por lo que no hay forma de que el basadofor
en rango funcione con él.¿Es eso para leer como, " Dime qué hace un rango (con matrices)? "
Responderé asumiendo eso: tome el siguiente ejemplo usando matrices anidadas:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (auto &pl : ia)
Versión de texto:
ia
es una matriz de matrices ("matriz anidada"), que contiene[3]
matrices, y cada una contiene[4]
valores. El ejemplo anterior recorreia
su 'rango' primario ([3]
) y, por lo tanto, repite[3]
tiempos. Cada ciclo produce uno deia
los[3]
valores primarios comenzando por el primero y terminando con el último: una matriz que contiene[4]
valores.pl
igual a{1,2,3,4}
- Una matrizpl
igual a{5,6,7,8}
- Una matrizpl
igual a{9,10,11,12}
- Una matrizAntes de explicar el proceso, aquí hay algunos recordatorios amigables sobre las matrices:
pl
debe ser una referencia porque no podemos copiar matricesn
es el número en cuestión, entoncesia[n]
es lo mismo que*(ia+n)
(estamos desreferenciando la dirección que sonn
entradas forward), yia+n
es lo mismo que&ia[n]
(Estamos obteniendo la dirección de esa entrada en la matriz).Esto es lo que está pasando:
pl
se establece como una referencia aia[n]
,n
igualando el recuento del bucle actual comenzando desde 0. Entonces,pl
estáia[0]
en la primera ronda, en la segunda estáia[1]
, y así sucesivamente. Recupera el valor mediante iteración.ia+n
sea menor queend(ia)
.... Y eso es todo.
En realidad, es solo una forma simplificada de escribir esto :
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int n = 0; n != 3; ++n) auto &pl = ia[n];
Si su matriz no está anidada, entonces este proceso se vuelve un poco más simple en el sentido de que no se necesita una referencia , porque el valor iterado no es una matriz sino un valor 'normal':
int ib[3] = {1,2,3}; // short for (auto pl : ib) cout << pl; // long for (int n = 0; n != 3; ++n) cout << ib[n];
Alguna información adicional
¿Qué pasa si no queremos usar la
auto
palabra clave al crearpl
? Como se veria eso?En el siguiente ejemplo, se
pl
refiere a unarray of four integers
. En cada buclepl
se le da el valoria[n]
:int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int (&pl)[4] : ia)
Y ... Así es como funciona, con información adicional para eliminar cualquier confusión. Es solo un
for
bucle 'taquigráfico' que cuenta automáticamente para usted, pero carece de una forma de recuperar el bucle actual sin hacerlo manualmente.fuente
Algunos ejemplos de código para demostrar la diferencia entre las matrices en Stack y las matrices en Heap
/** * Question: Can we use range based for built-in arrays * Answer: Maybe * 1) Yes, when array is on the Stack * 2) No, when array is the Heap * 3) Yes, When the array is on the Stack, * but the array elements are on the HEAP */ void testStackHeapArrays() { int Size = 5; Square StackSquares[Size]; // 5 Square's on Stack int StackInts[Size]; // 5 int's on Stack // auto is Square, passed as constant reference for (const auto &Sq : StackSquares) cout << "StackSquare has length " << Sq.getLength() << endl; // auto is int, passed as constant reference // the int values are whatever is in memory!!! for (const auto &I : StackInts) cout << "StackInts value is " << I << endl; // Better version would be: auto HeapSquares = new Square[Size]; Square *HeapSquares = new Square[Size]; // 5 Square's on Heap int *HeapInts = new int[Size]; // 5 int's on Heap // does not compile, // *HeapSquares is a pointer to the start of a memory location, // compiler cannot know how many Square's it has // for (auto &Sq : HeapSquares) // cout << "HeapSquare has length " << Sq.getLength() << endl; // does not compile, same reason as above // for (const auto &I : HeapInts) // cout << "HeapInts value is " << I << endl; // Create 3 Square objects on the Heap // Create an array of size-3 on the Stack with Square pointers // size of array is known to compiler Square *HeapSquares2[]{new Square(23), new Square(57), new Square(99)}; // auto is Square*, passed as constant reference for (const auto &Sq : HeapSquares2) cout << "HeapSquare2 has length " << Sq->getLength() << endl; // Create 3 int objects on the Heap // Create an array of size-3 on the Stack with int pointers // size of array is known to compiler int *HeapInts2[]{new int(23), new int(57), new int(99)}; // auto is int*, passed as constant reference for (const auto &I : HeapInts2) cout << "HeapInts2 has value " << *I << endl; delete[] HeapSquares; delete[] HeapInts; for (const auto &Sq : HeapSquares2) delete Sq; for (const auto &I : HeapInts2) delete I; // cannot delete HeapSquares2 or HeapInts2 since those arrays are on Stack }
fuente