Избыточность оператора delete[] в С++

1,00
р.
Тут я как всегда не вовремя задумался вот над каким вопросом. При выделении памяти из кучи очевидно, что в куче должна сохраняться информация о размере запрошенной области памяти и о количестве запрошенных элементов. Зачем же тогда существуют отдельно операторы delete и delete[]? Ведь независимо от того, запросили мы вектор или один элемент, в куче есть информация и о размере запрошенной области, и о количестве запрошенных элементов. Не может быть, чтобы при запросе одного элемента в куче не сохранялась информация о том, что запрошен один элемент. А если это так, то оператор delete вполне может разобраться (по служебной информации, содержащейся в куче) был ли запрошен массив или был запрошен один элемент. И, соответственно, вернуть в кучу память или одного элемента, или вектора. Получается, что оператор delete[] избыточен.
UPD1:
В нынешнем подходе С++ поступает более экономично: хранит свою дополнительную информацию только в массивах объектов с нетривиальным деструктором.
То есть Вы хотите сказать, что у кучи есть несколько форматов? Один формат для запрошенного одного элемента, другой формат для запрошенного вектора, третий формат для запрошенного одного элемента с нетривиальным аллокатором/деструктором, четвертый формат для запрошенного вектора с нетривиальным аллокатором/деструктором? Ну, это дело конечно разработчиков компилятора и кучи, как там они видят свою задачу чтобы сделать ее максимально эффективной. Но на первый взгляд иметь много форматов кучи это не так чтобы однозначно было эффективнее, чем хранить всю информацию и в рантайме разбираться, что же именно там сейчас лежит. Тем более, что разбираться в рантайме и при подходе со многими форматами кучи все равно придется.
UPD2:
И да, нетривиальный аллокатор тоже хранит в куче информацию о количестве запрошенных элементов и о размере одного элемента. Имея эту информацию нетривиальный деструктор может разобраться, что именно ему надо удалять. И опять же в этом случае не нужен оператор delete[], достаточно оператора delete.
UPD3:
Оставим пока в покое нетривиальные аллокаторы. Рассмотрим пока что стандартные аллокаторы, тем более что проблемы в обоих случаях одинаковы.
Итак, есть куча и есть операторы new и new[]. Оба оператора обязаны занести в служебную информацию кучи данные о размере одного объекта и о КОЛИЧЕСТВЕ ОБЪЕКТОВ в запросе. Соответственно, оператор возврата памяти delete нужен только один, так как по служебной информации рантайм может и должен разобраться сколько именно объектов было запрошено. Соответственно, оператор delete[] избыточен.
Теперь рассмотрим нестандартные (пользовательские) аллокаторы. Совершенно так же пользовательские new и new[] обязаны занести в служебную информацию кучи данные о размере одного объекта и о КОЛИЧЕСТВЕ ОБЪЕКТОВ в запросе. Дополнительно пользовательские new и new[] обязаны занести в служебную информацию кучи указатель на пользовательский деструктор. Опять же в этом случае оператор возврата памяти delete нужен только один, так как по служебной информации рантайм может и должен разобраться сколько именно объектов было запрошено. Соответственно, оператор delete[] избыточен.

Ответ
Во-первых, даже если в куче и сохраняется информация о размере запрошенного блока в байтах, способ хранения этой информации может быть известен delete только в том случае, если используется "штатный" аллокатор. Но процесс выделения "сырой" памяти в С++ является перегружаемым пользователем. Как только выделение памяти перешло на пользовательский аллокатор, delete уже не может определить размер блока.
Во-вторых, даже если размер блока в байтах известен, по этому размеру все равно нельзя однозначно восстановить точное количество элементов в массиве, чтобы вызвать точное количество деструкторов. Размер блока может превышать точное значение, требуемое для хранения элементов.
В-третьих, не ясно, о каком "количестве запрошенных элементов" вы говорите. Количество запрошенных элементов сохраняет именно new [] и вычитывает именно delete []. Для того, собственно, delete [] и сделан отдельным от delete. Смотрите детали здесь: Откуда C/C++ знает сколько надо освободить памяти, если не знает размер массива?
Теоретически можно сделать "умный delete", который сам всегда во всем разбирается. Но это приведет к безусловной необходимости хранить дополнительную информацию во всех блоках памяти. В нынешнем подходе С++ поступает более экономично: хранит свою дополнительную информацию только в массивах объектов с нетривиальным деструктором.
Фактически почти такой "умный delete" у вас уже есть. Никто вам не запрещает везде просто безусловно пользоваться new[]/delete[] и забыть про существование new/delete. То есть одиночные объекты просто выделять как массивы размера 1. Но это будет несколько более расточительно (и не поддерживает полиморфного удаления).

Отвечая на ваш UPD1:
В типичной реализации у блока в С++ куче фактически три формата: для одиночного объекта (new), для массива с тривиальными деструкторами (new[]) и для массива с нетривиальными деструкторами (new[]).
При этом первые два формата можно было бы считать совпадающими с точки зрения внутренней структуры, т.к. это просто "блоки памяти". Но тут вмешивается тот факт, что механизмы выделения/освобождения "сырой" памяти в С++ являются перегружаемыми пользователем: независимо для new/delete и для new[]/delete[]. Поэтому это - отдельные форматы.

Ответ на ваш UPD3:
Я не знаю, с чего вы взяли, что "Оба оператора обязаны занести в служебную информацию кучи данные о размере одного объекта и о КОЛИЧЕСТВЕ ОБЪЕКТОВ в запросе". Это совершенно не так.
Еще раз: просто new такой информации НЕ сохраняет. И new[] для типов с тривиальным деструктором никакой информации о количестве или размере объектов НЕ хранит тоже. Эти форматы просто выделяют память через обычный malloc, освобождают через обычный free и никакой дополнительной внутренней информации в этом блоке памяти не сохраняют. С точки зрения С++ памяти требуется ровно столько, сколько нужно для хранения пользовательских данных.
Особняком стоит только new[] для массива с нетривиальными деструкторами. Только он сохраняет в блоке служебную информацию о точном количестве элементов в массиве (и поэтому выделяет несколько больше памяти, чем требуется для пользовательских данных).
Информация о размере одного элемента в таком блоке не хранится вообще никогда - это низачем не нужно.
Я при этом говорю только о стандартных аллокаторах. Пользовательские аллокаторы тут ни при чем.