Давненько я не писал ничего на эту тему. Надо бы исправлять.
Templates and static polymorph, ja-ja!Итак, на этот раз я осмелюсь покуситься на святая святых ООП - на полиморфизм! Начнем с простого - с определения. Как известно любому мало-мальски грамотному в ООП программисту, "полиморфизм представляет собой способность связывать различные специфические виды поведения с помощью единой общей записи". Традиционно полиморфизм реализуется посредством переопределения в классе-наследнике некоторого виртуального метода. В результате, в коде, имеющем сведения только о классе базы, при обращении по указателю к некоторому перегруженному методу происходит разрешение перегрузки по таблице виртуальных методов и выбирается нужная реализация. Ввиду описанного этот механизм стал краеугольным камнем в ООП.
Однако рассмотрим минусы этого подхода:
1) На этапе исполнения при вызове наследованного метода делается обязательное обращение к таблице виртуальных функций. Да, сейчас эта процедура стала много быстрее, однако раньше это был просто кошмар по занимаемому времени.
2) Отсутствие информации о реализации класса-наследника для вызывающего кода порождает невозможность определения на этапе компиляции существования метода. Т.е. всегда есть опасность банального вызова неопределенного в данном классе абстрактного метода и разных радостей вроде Access violation, Abstract error или Pure virtual function call и все это на этапе исполнения!
3) При работе по указателю компилятор всегда делает косвенный вызов. Это значит, что о встраиваемых функциях можно забыть. Не шакув, знаете ли.
Теперь, если посмотреть внимательно на перечисленные минусы, можно заметить, что виной всему наследование (работа с указателями - как следствие) и если от него избавиться, то:
1) Не надо будет обращаться к таблице виртуальных функций. Да и создавать ее, в общем-то.
2) Компилятор сможет проверить правильность обращения, вопрос существования метода станет риторическим.
3) Компилятор сможет генерировать встраиваемые функции там, где это возможно.
Как это достижимо? Нужно только перенести связывание интерфейсов (функций/методов) с этапа выполнения на этап компиляции. А делается это с помощью шаблонов. Рассмотрим на примере кода:
Полиморфизм через наследование (динамический полиморфизм):
Вызывающий код:
Полиморфизм через шаблоны (статический полиморфизм):
Вызывающий код: