Saya agak terkejut bahwa tidak ada yang mengusulkan beberapa putaran berbasis sihir aritmatika untuk melakukan pekerjaan itu. Karena C. Wang sedang mencari solusi tanpa loop bersarang , saya akan mengusulkan satu:
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;
}
Nah, pendekatan ini tidak elegan dan fleksibel, jadi kita bisa mengemas semua proses ke dalam fungsi template:
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;
}
}
Fungsi template ini juga dapat diekspresikan dalam bentuk loop bersarang:
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);
}
}
}
}
Dan dapat digunakan untuk menyediakan larik 3D dengan ukuran sembarang ditambah nama fungsi, membiarkan deduksi parameter melakukan kerja keras untuk menghitung ukuran setiap dimensi:
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;
}
Ke arah yang lebih generik
Tetapi sekali lagi, ini kurang fleksibel karena hanya berfungsi untuk array 3D, tetapi dengan menggunakan SFINAE kita dapat melakukan pekerjaan untuk array dari dimensi arbitrer, pertama-tama kita memerlukan fungsi template yang mengulang array dengan peringkat 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);
}
}
Dan satu lagi yang mengulang array dari peringkat apa pun, melakukan rekursi:
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);
}
}
Hal ini memungkinkan kita untuk mengulang semua elemen di semua dimensi array berukuran arbitrer dimensi.
Bekerja dengan std::vector
Untuk beberapa vektor bersarang, solusinya merangkai salah satu dari larik berukuran arbitrer dimensi arbitrer, tetapi tanpa SFINAE: Pertama, kita memerlukan fungsi templat yang mengulangi std::vector
s dan memanggil fungsi yang diinginkan:
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);
}
}
Dan fungsi template lain yang mengiterasi segala jenis vektor dan menyebut dirinya sendiri:
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);
}
}
Terlepas dari level bersarang, iterate_all
akan memanggil versi vektor-vektor kecuali versi vektor-nilai-nilai lebih cocok sehingga mengakhiri rekursivitas.
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;
}
Menurut saya fungsi body cukup sederhana dan lurus ke depan ... Saya ingin tahu apakah kompilator dapat membuka gulungan ini (saya hampir yakin bahwa sebagian besar kompiler dapat membuka gulungan contoh pertama).
Lihat demo langsung di sini .
Semoga membantu.