c++ - instead - ¿Cómo funciona el rango de trabajo para arreglos simples?
qt foreach (5)
En C ++ 11 puede usar un rango for
, que actúa como el foreach
de otros idiomas. Funciona incluso con arreglos C simples:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
¿Cómo sabe cuándo detenerse? ¿Funciona solo con matrices estáticas que se han declarado en el mismo ámbito en el for
se utiliza? ¿Cómo usarías esto for
las matrices dinámicas?
¿Cómo funciona el rango de trabajo para arreglos simples?
¿Es eso para leer como, " Dime qué hace ranged-for (con matrices)? "
Responderé asumiendo que: 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, cada una con [4]
valores. El ejemplo anterior recorre ia
por su ''rango'' primario ( [3]
) y, por lo tanto, repite [3]
veces. Cada ciclo produce uno de los principales valores de ia
'' [3]
comenzando desde el primero y finalizando con el último: un conjunto que contiene [4]
valores.
- Primer ciclo:
pl
es igual a{1,2,3,4}
- Una matriz - Segundo ciclo:
pl
es igual a{5,6,7,8}
- Una matriz - Tercer ciclo:
pl
es igual a{9,10,11,12}
- Una matriz
Antes de explicar el proceso, aquí hay algunos recordatorios amistosos sobre las matrices:
- Las matrices se interpretan como punteros a su primer valor: el uso de una matriz sin iteración devuelve la dirección del primer valor
-
pl
debe ser una referencia porque no podemos copiar matrices - Con las matrices, cuando agrega un número al objeto de la matriz en sí, avanza muchas veces y ''señala'' a la entrada equivalente - Si
n
es el número en cuestión, entoncesia[n]
es lo mismo que*(ia+n)
(Estamos desreferenciando la dirección que tienen
entradas adelante), eia+n
es igual que&ia[n]
(estamos obteniendo la dirección de esa entrada en la matriz).
Esto es lo que está pasando:
- En cada bucle,
pl
se establece como una referencia aia[n]
, conn
igual al recuento de bucle de corriente a partir de 0. Entonces,pl
esia[0]
en la primera ronda, en el segundo esia[1]
y pronto. Recupera el valor por iteración. - El ciclo continúa siempre que
ia+n
sea menor queend(ia)
.
... Y eso es todo.
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é sucede si no queremos usar la palabra clave auto
al crear pl
? Como se veria eso?
En el siguiente ejemplo, pl
refiere a una array of four integers
. En cada bucle, pl
recibe el valor ia[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 ''atajo'' for
bucle que cuenta automáticamente para usted, pero le falta una manera de recuperar el bucle actual sin hacerlo manualmente.
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 yo 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 la variable. Un compilador tiene que saber el tipo.
Como C ++ 11 std::extent
se puede usar 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 tiene que 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 pone más interesante:
char v[] { ''A'', ''B'', ''C'', ''D'' };
int size2{ std::extent< decltype(v) >::value };
std::cout << "Array size: " << size2 << std::endl;
De acuerdo con el último Borrador de Trabajo de C ++ (n3376), el alcance del enunciado es equivalente al 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 ciclo regular for
usar iteradores.
Creo que puede estar buscando algo como lo siguiente para proporcionar una forma de utilizar la sintaxis anterior con matrices que constan de solo 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 a distancia . 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ías usando std::for_each
o un ciclo simple.
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 la expresión pasada a la derecha de :
es un tipo de matriz, el ciclo itera de ptr
a ptr + size
( ptr
apunta al primer elemento de la matriz, siendo el size
el conteo de elementos de la matriz).
Esto contrasta con los tipos definidos por el usuario, que funcionan al buscar begin
y end
como miembros si pasa un objeto de clase o (si no hay miembros llamados de esa manera) funciones no miembro. Esas funciones producirán los iteradores de inicio y final (señalando directamente después del último elemento y el comienzo de la secuencia, respectivamente).
Esta pregunta aclara por qué existe esa diferencia.
Sabe cuándo parar 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 se itera sobre matrices estáticas, de manera informal, el compilador busca los nombres de begin
end
en el alcance de la clase del objeto sobre el que itera, o busca el begin(range)
y el end(range)
usando búsqueda dependiente de argumento y los utiliza como iteradores.
Para más información, en el estándar C ++ 11 (o borrador público del mismo), "6.5.4 El enunciado for
basado for
rango", pág.145