(Справочная информация: у меня есть некоторый опыт реализации компиляторов C и C ++.)
Массивы переменной длины в C99 были в основном ошибкой. Чтобы поддержать VLA, C99 должен был пойти на следующие уступки здравому смыслу:
sizeof x
больше не всегда константа времени компиляции; иногда компилятор должен генерировать код для оценки sizeof
-экспрессии во время выполнения.
Разрешение двухмерного Власа ( int A[x][y]
) требуется новый синтаксис для объявления функций , которые используют 2D Влас в качестве параметров: void foo(int n, int A[][*])
.
Менее важно в мире C ++, но чрезвычайно важно для целевой аудитории программистов встраиваемых систем на C, объявление VLA означает разбрасывание произвольно большого куска вашего стека. Это гарантированное переполнение стека и сбой. (Каждый раз, когда вы объявляете int A[n]
, вы неявно утверждаете, что у вас есть 2 ГБ стека, чтобы сэкономить. В конце концов, если вы знаете, что « n
здесь определенно меньше 1000», то вы просто объявите int A[1000]
. Подстановка 32-разрядного целого числа n
для 1000
- это допуск что вы понятия не имеете, каким должно быть поведение вашей программы.)
Хорошо, теперь давайте перейдем к разговору о C ++. В C ++ мы имеем такое же сильное различие между «системой типов» и «системой ценностей», как в C89… но мы действительно начали полагаться на него так, как это не делает C. Например:
template<typename T> struct S { ... };
int A[n];
S<decltype(A)> s; // equivalently, S<int[n]> s;
Если бы n
не была константа времени компиляции (т. A
Е. Если бы она была изменяемого типа), то что это за тип S
? Будет ли S
тип также определяться только во время выполнения?
Как насчет этого:
template<typename T> bool myfunc(T& t1, T& t2) { ... };
int A1[n1], A2[n2];
myfunc(A1, A2);
Компилятор должен сгенерировать код для некоторой реализации myfunc
. Как должен выглядеть этот код? Как мы можем статически генерировать этот код, если мы не знаем тип A1
во время компиляции?
Хуже того, что если во время выполнения окажется, что n1 != n2
, так что !std::is_same<decltype(A1), decltype(A2)>()
? В этом случае вызов myfunc
даже не должен компилироваться , потому что вывод типа шаблона должен завершиться неудачей! Как мы можем подражать этому поведению во время выполнения?
По сути, C ++ движется в направлении внедрения все большего количества решений во время компиляции : генерация кода шаблона, constexpr
оценка функции и так далее. Тем временем C99 был занят внедрением традиционных решений времени компиляции (например sizeof
) во время выполнения . Имея это в виду, действительно ли он даже имеет смысл затрачивать никаких усилий , пытаясь интегрировать Власа C99 стиле в C ++?
Как уже отмечали все остальные авторы, C ++ предоставляет множество механизмов выделения кучи ( std::unique_ptr<int[]> A = new int[n];
или std::vector<int> A(n);
являющихся очевидными), когда вы действительно хотите донести идею: «Я понятия не имею, сколько ОЗУ мне может понадобиться». А C ++ предоставляет изящную модель обработки исключений для решения неизбежной ситуации, когда объем необходимой вам оперативной памяти больше, чем у вас. Но, надеюсь, этот ответ дает вам хорошее представление о том, почему VLA в стиле C99 не очень подходят для C ++ - и даже не совсем подходят для C99. ;)
Для получения дополнительной информации по этой теме см. N3810 «Альтернативы для расширений массивов» , документ Bjarne Stroustrup за октябрь 2013 года о VLA. POV Бьярне очень отличается от моего; N3810 больше фокусируется на поиске хорошего синтаксиса C ++ для вещей и на препятствовании использованию сырых массивов в C ++, тогда как я больше сосредоточился на последствиях для метапрограммирования и системы типов. Я не знаю, считает ли он последствия метапрограммирования / системы типов решенными, разрешимыми или просто неинтересными.
Хорошая запись в блоге, которая затрагивает многие из этих пунктов, - «Законное использование массивов переменной длины» (Chris Wellons, 2019-10-27).