Потому что все размеры кроме последнего обязательно фиксированы. Как только вам нужны обе размерности переменными, приходится переходить к более общей схеме на основе одномерного массива.
Вы, вероятно, имеете в виду статические массивы (поскольку в C++ нет многомерных динамических массивов). Однако про статические массивы многое известно уже на этапе компиляции, поэтому все размеры можно "освободить" с помощью шаблонов:
#include <iostream>
#include <iomanip>
template <size_t n, size_t m>
void fill_array(int (&x)[n][m])
{
int k = 0;
for (size_t i = 0; i < n; ++i)
{
for (size_t j = 0; j < m; ++j)
{
x[i][j] = ++k;
}
}
}
template <size_t n, size_t m>
void print_array(const int (&x)[n][m])
{
std::cout << "n = " << n << ", m = " << m << std::endl;
for (size_t i = 0; i < n; ++i)
{
for (size_t j = 0; j < m; ++j)
{
std::cout << std::setw(5) << x[i][j];
}
std::cout << std::endl;
}
std::cout << std::endl;
}
int main()
{
int a[2][5];
int b[4][3];
fill_array(a);
print_array(a);
fill_array(b);
print_array(b);
}
Щас проверил на простом тесте на gcc. Вариант с многомерным массивом чуть быстрее при рандомном доступе и сильно быстрее при последовательном (хотя, конечно, если при последовательном доступе в "одномерном" варианте добавить бегущий пойнтер, то наверно будет одинаково).
Что ж, теперь можно расслабиться и спокойно продолжать пользоваться многомерными массивами.