En C ++ 11 puede usar un rango basado en for, que actúa como el foreachde 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 forse utiliza? ¿Cómo usarías esto forcon 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.numbersessizeof(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 deptraptr + size(ptrapuntando al primer elemento de la matriz, quesizees el recuento de elementos de la matriz).Esto contrasta con los tipos definidos por el usuario, que funcionan mirando hacia arriba
beginyendcomo 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 thatstd :: begin`std::enduse 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::extentse 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
decltypey 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
forbucle 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_eachunforbucle 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
beginyenden 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
fordeclaració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 basadoforen 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:
iaes una matriz de matrices ("matriz anidada"), que contiene[3]matrices, y cada una contiene[4]valores. El ejemplo anterior recorreiasu 'rango' primario ([3]) y, por lo tanto, repite[3]tiempos. Cada ciclo produce uno deialos[3]valores primarios comenzando por el primero y terminando con el último: una matriz que contiene[4]valores.pligual a{1,2,3,4}- Una matrizpligual a{5,6,7,8}- Una matrizpligual a{9,10,11,12}- Una matrizAntes de explicar el proceso, aquí hay algunos recordatorios amigables sobre las matrices:
pldebe ser una referencia porque no podemos copiar matricesnes el número en cuestión, entoncesia[n]es lo mismo que*(ia+n)(estamos desreferenciando la dirección que sonnentradas forward), yia+nes lo mismo que&ia[n](Estamos obteniendo la dirección de esa entrada en la matriz).Esto es lo que está pasando:
plse establece como una referencia aia[n],nigualando el recuento del bucle actual comenzando desde 0. Entonces,plestáia[0]en la primera ronda, en la segunda estáia[1], y así sucesivamente. Recupera el valor mediante iteración.ia+nsea 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
autopalabra clave al crearpl? Como se veria eso?En el siguiente ejemplo, se
plrefiere a unarray of four integers. En cada bucleplse 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
forbucle '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