Ero un po 'scioccato dal fatto che nessuno avesse proposto un ciclo basato sulla magia aritmetica per fare il lavoro. Dato che C. Wang sta cercando una soluzione senza loop annidati , ne proporrò una:
double B[10][8][5];
int index = 0;
while (index < (10 * 8 * 5))
{
const int x = index % 10,
y = (index / 10) % 10,
z = index / 100;
do_something_on_B(B[x][y][z]);
++index;
}
Bene, questo approccio non è elegante e flessibile, quindi potremmo raggruppare tutto il processo in una funzione modello:
template <typename F, typename T, int X, int Y, int Z>
void iterate_all(T (&xyz)[X][Y][Z], F func)
{
const int limit = X * Y * Z;
int index = 0;
while (index < limit)
{
const int x = index % X,
y = (index / X) % Y,
z = index / (X * Y);
func(xyz[x][y][z]);
++index;
}
}
Questa funzione modello può essere espressa anche sotto forma di cicli annidati:
template <typename F, typename T, int X, int Y, int Z>
void iterate_all(T (&xyz)[X][Y][Z], F func)
{
for (auto &yz : xyz)
{
for (auto &z : yz)
{
for (auto &v : z)
{
func(v);
}
}
}
}
E può essere utilizzato fornendo un array 3D di dimensioni arbitrarie più il nome della funzione, lasciando che la deduzione del parametro faccia il duro lavoro di contare la dimensione di ciascuna dimensione:
int main()
{
int A[10][8][5] = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};
int B[7][99][8] = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};
iterate_all(A, do_something_on_A);
iterate_all(B, do_something_on_B);
return 0;
}
Verso più generico
Ma ancora una volta, manca di flessibilità perché funziona solo per array 3D, ma usando SFINAE possiamo fare il lavoro per array di una dimensione arbitraria, prima abbiamo bisogno di una funzione template che itera array di rango 1:
template<typename F, typename A>
typename std::enable_if< std::rank<A>::value == 1 >::type
iterate_all(A &xyz, F func)
{
for (auto &v : xyz)
{
func(v);
}
}
E un altro che itera array di qualsiasi rango, eseguendo la ricorsione:
template<typename F, typename A>
typename std::enable_if< std::rank<A>::value != 1 >::type
iterate_all(A &xyz, F func)
{
for (auto &v : xyz)
{
iterate_all(v, func);
}
}
Questo ci permette di iterare tutti gli elementi in tutte le dimensioni di un array di dimensioni arbitrarie.
Lavorando con std::vector
Per il vettore annidato multiplo, la soluzione riassembla quella di un array di dimensioni arbitrarie, ma senza SFINAE: per prima cosa avremo bisogno di una funzione modello che itera se std::vectorchiama la funzione desiderata:
template <typename F, typename T, template<typename, typename> class V>
void iterate_all(V<T, std::allocator<T>> &xyz, F func)
{
for (auto &v : xyz)
{
func(v);
}
}
E un'altra funzione modello che itera qualsiasi tipo di vettore di vettori e si chiama:
template <typename F, typename T, template<typename, typename> class V>
void iterate_all(V<V<T, std::allocator<T>>, std::allocator<V<T, std::allocator<T>>>> &xyz, F func)
{
for (auto &v : xyz)
{
iterate_all(v, func);
}
}
Indipendentemente dal livello di annidamento, iterate_allchiamerà la versione del vettore di vettori a meno che la versione del vettore di valori non sia una corrispondenza migliore, ponendo così fine alla ricorsività.
int main()
{
using V0 = std::vector< std::vector< std::vector<int> > >;
using V1 = std::vector< std::vector< std::vector< std::vector< std::vector<int> > > > >;
V0 A0 = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};
V1 A1 = {{{{{9, 8}, {7, 6}}, {{5, 4}, {3, 2}}}}};
iterate_all(A0, do_something_on_A);
iterate_all(A1, do_something_on_A);
return 0;
}
Penso che il corpo della funzione sia piuttosto semplice e diretto ... Mi chiedo se il compilatore possa srotolare questi cicli (sono quasi sicuro che la maggior parte dei compilatori potrebbe srotolare il primo esempio).
Guarda la demo live qui .
Spero che sia d'aiuto.