Ich war ein bisschen schockiert, dass niemand eine auf Arithmetik-Magie basierende Schleife vorgeschlagen hat, um die Arbeit zu erledigen. Da C. Wang nach einer Lösung ohne verschachtelte Schleifen sucht , schlage ich eine vor:
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;
}
Nun, dieser Ansatz ist nicht elegant und flexibel, daher könnten wir den gesamten Prozess in eine Vorlagenfunktion packen:
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;
}
}
Diese Vorlagenfunktion kann auch in Form von verschachtelten Schleifen ausgedrückt werden:
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);
}
}
}
}
Und kann verwendet werden, um ein 3D-Array mit beliebiger Größe und dem Funktionsnamen bereitzustellen, sodass der Parameterabzug die harte Arbeit des Zählens der Größe jeder Dimension erledigt:
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;
}
Auf dem Weg zu mehr Generika
Aber auch hier mangelt es an Flexibilität, da es nur für 3D-Arrays funktioniert. Mit SFINAE können wir jedoch für Arrays beliebiger Dimension arbeiten. Zuerst benötigen wir eine Vorlagenfunktion, die Arrays mit Rang 1 iteriert :
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);
}
}
Und noch eine, die Arrays beliebigen Ranges iteriert und die Rekursion durchführt:
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);
}
}
Dies ermöglicht es uns, alle Elemente in allen Dimensionen eines Arrays beliebiger Dimensionen und beliebiger Größe zu iterieren.
Arbeiten mit std::vector
Für den mehrfach verschachtelten Vektor ähnelt die Lösung der eines Arrays mit beliebiger Dimension und beliebiger Größe, jedoch ohne SFINAE: Zuerst benötigen wir eine Vorlagenfunktion, die std::vector
s iteriert und die gewünschte Funktion aufruft:
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);
}
}
Und eine andere Vorlagenfunktion, die jede Art von Vektorvektor iteriert und sich selbst aufruft:
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);
}
}
Unabhängig von der Verschachtelungsebene iterate_all
wird die Vektor-Vektor-Version aufgerufen, es sei denn, die Vektor-Wert-Version passt besser zusammen, wodurch die Rekursivität beendet wird.
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;
}
Ich denke, dass der Funktionskörper ziemlich einfach und unkompliziert ist ... Ich frage mich, ob der Compiler diese Schleifen abrollen könnte (ich bin fast sicher, dass die meisten Compiler das erste Beispiel abrollen könnten).
Sehen Sie hier die Live-Demo .
Ich hoffe es hilft.