Основная причина заключается в том, что сохранение vtable в качестве детали реализации позволяет любой конкретной реализации оптимизировать ее по своему усмотрению; это означает, что он может, например, обрезать или даже полностью исключить vtable, если он может доказать, что для данного метода (или всех методов) нет виртуальных вызовов. Или он может заменить отправку vtable проверкой типа if-else, если, например, он видит, что есть только несколько альтернатив (это может быть выгодно, потому что предсказание ветвлений будет работать в этом случае, но не с vtables, а также потому, что тогда могут быть встроены ветви if-else). Он может переупорядочивать методы в vtable так, что наиболее часто вызываемые из них появляются раньше, или так, что те, которые обычно вызываются один за другим, заполняют смежные слоты в vtable, чтобы использовать преимущества кэширования. И так далее. Конечно, все эти реализации также сделали бы макет vtable совершенно непредсказуемым и, следовательно, бесполезным, если бы он подвергался (в соответствии со спецификацией языка) реализации.
Кроме того, vtables не так просты, как кажется. Например, компиляторам часто приходится генерировать thunks для исправления указателя this
для таких вещей, как виртуальное наследование или множественное наследование в сочетании с ковариантными типами возврата. Это опять-таки то, что не имеет «единственного лучшего способа» сделать это (именно поэтому разные компиляторы делают это по-разному), и его стандартизация фактически потребует выбора конкретного способа.
Тем не менее, "переключение vtable" является потенциально полезным методом, если он представлен как конструкция более высокого уровня (так что оптимизации все еще возможны). Для примера см. UnrealScript, который позволяет определить несколько состояний для класса (один по умолчанию, другой с именем) и переопределить некоторые методы в именованных состояниях. Производные классы могут переопределять больше методов в существующих состояниях или добавлять свои собственные состояния и переопределять их. Кроме того, состояния могут расширять другие состояния (поэтому, если метод не переопределяется для определенного состояния, он возвращается к «родительскому» состоянию и т. Д., Пока цепочка не достигнет состояния по умолчанию). Для актерского моделирования (какими по сути являются игры) все это имеет большой смысл, поэтому в UnrealScript оно есть. И очевидный эффективный механизм реализации всего этого - переключение виртуальных таблиц, причем каждое состояние имеет отдельный виртуальный каталог.