J'étais un peu choqué que personne n'ait proposé une boucle basée sur la magie arithmétique pour faire le travail. Puisque C. Wang recherche une solution sans boucles imbriquées , je vais en proposer une:
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;
}
Eh bien, cette approche n'est ni élégante ni flexible, nous pourrions donc regrouper tout le processus dans une fonction de modèle:
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;
}
}
Cette fonction de modèle peut également être exprimée sous la forme de boucles imbriquées:
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);
}
}
}
}
Et peut être utilisé en fournissant un tableau 3D de taille arbitraire plus le nom de la fonction, laissant la déduction de paramètre faire le dur travail de compter la taille de chaque dimension:
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;
}
Vers plus générique
Mais encore une fois, cela manque de flexibilité car cela ne fonctionne que pour les tableaux 3D, mais en utilisant SFINAE nous pouvons faire le travail pour des tableaux d'une dimension arbitraire, nous avons d'abord besoin d'une fonction de modèle qui itère des tableaux de rang 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);
}
}
Et un autre qui itère des tableaux de n'importe quel rang, faisant la récursion:
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);
}
}
Cela nous permet d'itérer tous les éléments dans toutes les dimensions d'un tableau de dimensions arbitraires de taille arbitraire.
Travailler avec std::vector
Pour le vecteur imbriqué multiple, la solution ressemble à celle d'un tableau de taille arbitraire de dimensions arbitraires, mais sans SFINAE: nous aurons d'abord besoin d'une fonction modèle qui itère std::vector
s et appelle la fonction souhaitée:
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);
}
}
Et une autre fonction de modèle qui itère tout type de vecteur de vecteurs et s'appelle lui-même:
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);
}
}
Quel que soit le niveau d'imbrication, iterate_all
appellera la version de vecteur de vecteurs à moins que la version de vecteur de valeurs soit une meilleure correspondance, mettant ainsi fin à la récursivité.
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;
}
Je pense que le corps de la fonction est assez simple et direct ... Je me demande si le compilateur pourrait dérouler ces boucles (je suis presque sûr que la plupart des compilateurs pourraient dérouler le premier exemple).
Voir la démo en direct ici .
J'espère que ça aide.