Собственно общался тут с коллегами по поводу двойной диспетчеризации (double-dipatching), ну они говорят — в C++ лучше запихать это дело в шаблоны, будет гораздо быстрее и меньше ошибок в рантайме.
Соответственно 2 вопроса:
какие плюсы и минусы шаблонов против "традиционной" реализации посредством 2 vtbl?
И вообще — можно ли совсем заменить полиморфизм и виртуальные функции шаблонами.
Т.е. интересуют ситуации более выгодные для полиморфизма в compile-time и полиморфизма "настоящего", т.е. посредством виртуальных функций и vtbl?
Буду рад любым мнениям (даже помидорам в мою сторону)
P.S. коллеги плюсовики, а я в данный момент занимаюсь дельфи, где шаблонов нема, увы...
Здравствуйте, Курилка, Вы писали:
К>Собственно общался тут с коллегами по поводу двойной диспетчеризации (double-dipatching), ну они говорят — в C++ лучше запихать это дело в шаблоны, будет гораздо быстрее и меньше ошибок в рантайме. К>Соответственно 2 вопроса: К>какие плюсы и минусы шаблонов против "традиционной" реализации посредством 2 vtbl? К>И вообще — можно ли совсем заменить полиморфизм и виртуальные функции шаблонами. К>Т.е. интересуют ситуации более выгодные для полиморфизма в compile-time и полиморфизма "настоящего", т.е. посредством виртуальных функций и vtbl?
Сравнение разного функционала. Шаблоны преследуют цель инкапсулировать некоторый алгоритм или набор алгоритмов в классе, который применяются к нескольким другим классам, не обязательно связанными общим предком. Выполняется только на этапе компиляции. Наследование и позднее связывание — наследование общих интерфейсов от наследованных классов, и определение этих интерфейсов на этапе выполнения. То есть, в первом случае, мы имеем класс отвечающий за некоторый алгоритм, имеющий свой собственный интерфейс определяемый только на этапу компиляции, который компилируется в объект отвечающий за реализацию алгоритма для конкретного типа класса. И соответсвенно, Alg<class 1> и Alg<class 2> совершенно различные типы. Во втором случае, мы получаем объект реализующий свойства, имеющий некоторую библиотеку типов (интерфейсов) которые можно идентифицировать в Runtime. Например, если посмотреть на паттерн Composite, то такие вещи возможны только при использовании наследования, в котором возможна идентификация интерфейсов.
Также говорят что наследование некоторое отступления от полиформизма, так как наследуемый объект предоставляет свои внутренние свойсва для наследников.
Здравствуйте, GlebZ, Вы писали:
GZ>Здравствуйте, Курилка, Вы писали:
К>>Собственно общался тут с коллегами по поводу двойной диспетчеризации (double-dipatching), ну они говорят — в C++ лучше запихать это дело в шаблоны, будет гораздо быстрее и меньше ошибок в рантайме. К>>Соответственно 2 вопроса: К>>какие плюсы и минусы шаблонов против "традиционной" реализации посредством 2 vtbl? К>>И вообще — можно ли совсем заменить полиморфизм и виртуальные функции шаблонами. К>>Т.е. интересуют ситуации более выгодные для полиморфизма в compile-time и полиморфизма "настоящего", т.е. посредством виртуальных функций и vtbl? GZ>Сравнение разного функционала. Шаблоны преследуют цель инкапсулировать некоторый алгоритм или набор алгоритмов в классе, который применяются к нескольким другим классам, не обязательно связанными общим предком. Выполняется только на этапе компиляции. Наследование и позднее связывание — наследование общих интерфейсов от наследованных классов, и определение этих интерфейсов на этапе выполнения. То есть, в первом случае, мы имеем класс отвечающий за некоторый алгоритм, имеющий свой собственный интерфейс определяемый только на этапу компиляции, который компилируется в объект отвечающий за реализацию алгоритма для конкретного типа класса. И соответсвенно, Alg<class 1> и Alg<class 2> совершенно различные типы. Во втором случае, мы получаем объект реализующий свойства, имеющий некоторую библиотеку типов (интерфейсов) которые можно идентифицировать в Runtime. Например, если посмотреть на паттерн Composite, то такие вещи возможны только при использовании наследования, в котором возможна идентификация интерфейсов.
Сенкс за пример!
Т.е. имеем в случае шаблонов — больший объём бинарника, а в случае наследования — накл. расходы, связанные с vtbl?
Не говоря о невозможности нек. конструкций в 1-м или 2-м подходах...
GZ>Также говорят что наследование некоторое отступления от полиформизма, так как наследуемый объект предоставляет свои внутренние свойсва для наследников.
Т.е. от полиморфизма с т.зр. ФП? Где тот же sort может быть для всех типов без никаких ограничений на тип объектов?
Не знаешь, а есть где-нибудь строгое определение полиморфизма как такогого, ну и рассмотрение разн. вариантов было бы также интересно
<...>
[ИМХО]
Главный плюс позднего связывания в сокрашении зависимостей между модулями.
Для использования варианта с шаблонами, клиентская сторона должна знать про все классы, над которыми она собирается учинить полиморфное использование. Причем, она должна знать ещу и про все классы, которые являются свойствами этих классов, и про свойства свойств и т.д.
Если в случае отдельного пакета это себе ещё можно позволить, то для выстраивания межпакетного интерфейсы я бы всё-таки предпочел vtbl
[/ИМХО]
run-time-полиморфизм требует
* затрат на его поддержку (память под vtbl, vptr или аналогичные рукоделия; время на косвенный вызов; косвенный доступ к данным)
* бОльшей строгости интерфейсов (поскольку структура vtbl прибита гвоздями)
Чистый compile-time полиморфизм, в свою очередь, требует, чтобы типы были известны заранее. А это, зачастую, неоправданно ограничивает программиста. Например, вместо массива указателей на полиморфные объекты — получаем кортеж разнотипных объектов (по указателю или по значению — не суть важно).
Естественно, что оптимум — это использовать и то, и другое вместе.
Кстати говоря. Когда рассматриваешь рантайм-полиморфизм, не надо ограничивать себя "жёсткими" реализациями — виртуальными функциями. Есть ещё способы добиться похожих результатов — например, указатели на функции.
Так, шаблон boost::function для каждого случая заводит указатель на функцию-манипулятор, которую получает из шаблона функции. А Loki::Functor — объект класса-манипулятора по шаблону.
В результате у boost получается непрозрачный, трудно отлаживаемый, но достаточно компактный код. А у Loki — туча классов, что что ведёт к разбуханию RTTI.
К>run-time-полиморфизм требует К>* затрат на его поддержку (память под vtbl, vptr или аналогичные рукоделия; время на косвенный вызов; косвенный доступ к данным)
При правильном подходе, все можно испортить.
1. При compile-time генерация большого числа классов и соответсвенно расходы памяти CODE.
2. При compile-time доступ имеется только к открытому интерфейсу, соответсвенно невозможность работы с внутренними функциями и соответсвенно может пострадать время выполнения. К тому же тип сам по себе может быть виртуальным.
Так что все это относительно
К>Естественно, что оптимум — это использовать и то, и другое вместе. Истина.
Более корректно говорить о динамическом полиморфизме и о статическом полиморфизме.
Тогда все становится просто и ясно. Динамический требует затрат времени на проверки. Статический требует все проверки осуществить во время компиляции.
К>run-time-полиморфизм требует К>* затрат на его поддержку (память под vtbl, vptr или аналогичные рукоделия; время на косвенный вызов; косвенный доступ к данным) К>* бОльшей строгости интерфейсов (поскольку структура vtbl прибита гвоздями)
Втбл — это одна из реализаций. Вместо ВТБЛ можно с успехом использовать if-ы или switch-и.
К>Чистый compile-time полиморфизм, в свою очередь, требует, чтобы типы были известны заранее.
Тут неопределено понятие "известны заранее". Если заранее означает до реализации полиморфного класса/алгоритма, то утвреждение не верно. Если заранее означает до кмпиляции в непосредственный исполняемый код, то верно. Так те же шаблоны можно использовать с типами созданными позже чем шаблоны. Так же код может быть преобразован в некий промежуточный вариант (вроде МСИЛ-а или байт-кода) который будет поддерживать полиморфизм превращая таким образом динамический полиморфизм в статический.
К> А это, зачастую, неоправданно ограничивает программиста. Например, вместо массива указателей на полиморфные объекты — получаем кортеж разнотипных объектов (по указателю или по значению — не суть важно).
Проблема так же решается применением разных техник. Например, аля Variant в VB или any в корбе.
К>Естественно, что оптимум — это использовать и то, и другое вместе.
На самом деле оптимальным решением было бы переложить решение о применение некоторого метода на компилятор. По хорошему бы программист должен выражать намерение, а компилятор реализовывать его оптимальным образом (учитывая все нюансы). К сожалению современные языки и компиляторы редко задумываются об оптимальности и удобстве одновременно.
Например, возмем две сущности функционалы в функцональных языках (функции высшего порядка) и делегаты в Шарпе. По сути с их помщью можно решать одинаковые задачи. Только делегаты всегда будут динамическим (рантаймным) решением, а функционалы статическим. Но ведь, с учетом особенностей дотнета (промежуточный язык и его джит-компиляция) можно было бы во многих случаях устранять рантайм-проверки переписывая код соотвествующим образом при джит-компиляции.
Думаю когда нибудь все прийдет к этому. А пока все это только теория.
К>Кстати говоря. Когда рассматриваешь рантайм-полиморфизм, не надо ограничивать себя "жёсткими" реализациями — виртуальными функциями. Есть ещё способы добиться похожих результатов — например, указатели на функции.
Таких способов много.
К>Так, шаблон boost::function для каждого случая заводит указатель на функцию-манипулятор, которую получает из шаблона функции. А Loki::Functor — объект класса-манипулятора по шаблону. К>В результате у boost получается непрозрачный, трудно отлаживаемый, но достаточно компактный код. А у Loki — туча классов, что что ведёт к разбуханию RTTI.
Ну, разбугание и туча классов — это из-за того что средство не встроено в язык, а эмулируется подручными средсвами совсем для этого не предназначеными.
Намного стрешнее проблема которую я описал выше. Ведь Loki::Functor и boost::function решают — это аналоги фанкторов в ФЯ и делегатов в дотнете. А это суть разные реализации одной и той же абстракции. И оптимально было бы отложить выбор реализации до того момента когда будет четко ясно какя из них более приемлема. А так же снять брямя выбора с плеч программиста.
... << RSDN@Home 1.1.4 beta 3 rev. 206>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Курилка, Вы писали:
К>Не знаешь, а есть где-нибудь строгое определение полиморфизма как такогого, ну и рассмотрение разн. вариантов было бы также интересно
Здравствуйте, VladD2, Вы писали:
К>>run-time-полиморфизм требует К>>* затрат на его поддержку (память под vtbl, vptr или аналогичные рукоделия; время на косвенный вызов; косвенный доступ к данным) К>>* бОльшей строгости интерфейсов (поскольку структура vtbl прибита гвоздями)
VD>Втбл — это одна из реализаций. Вместо ВТБЛ можно с успехом использовать if-ы или switch-и.
... и брать/вычислять тэг из данных объекта.
К>>Чистый compile-time полиморфизм, в свою очередь, требует, чтобы типы были известны заранее.
VD>Тут неопределено понятие "известны заранее". Если заранее означает до реализации полиморфного класса/алгоритма, то утвреждение не верно. Если заранее означает до кмпиляции в непосредственный исполняемый код, то верно. Так те же шаблоны можно использовать с типами созданными позже чем шаблоны. Так же код может быть преобразован в некий промежуточный вариант (вроде МСИЛ-а или байт-кода) который будет поддерживать полиморфизм превращая таким образом динамический полиморфизм в статический.
К>> А это, зачастую, неоправданно ограничивает программиста. Например, вместо массива указателей на полиморфные объекты — получаем кортеж разнотипных объектов (по указателю или по значению — не суть важно).
VD>Проблема так же решается применением разных техник. Например, аля Variant в VB или any в корбе.
Это всё тот же динамический полиморфизм. Где-то будет switch(vtType)...
Здравствуйте, Кодт, Вы писали:
К>... и брать/вычислять тэг из данных объекта.
Естественно. Какая же динамика без изменяющихся данных?
К>Это всё тот же динамический полиморфизм. Где-то будет switch(vtType)...
Я и не спорю. Я о том, что реализация может быть не одна. А динамика от статики и отличается только тем, что в рантайме анализируеются какие-то данные.
Просто забавно слышать что код типа if (a) ... не требует поддержки в рантайме. А a.b() требует, так как b виртуальная функция.
... << RSDN@Home 1.1.4 beta 3 rev. 206>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Втбл — это одна из реализаций. Вместо ВТБЛ можно с успехом использовать if-ы или switch-и.
В общем случае это не так. Для использования if или switch компилятору необходимо знать во время компиляции модуля обо ВСЕХ потомках обрабатываемого базового класса. Это требование, как правило, несовместимо с реальной жизнью
Короче, if/switch далеко не эквивалентны vtbl'у.
Здравствуйте, prVovik, Вы писали:
V>Это требование, как правило, несовместимо с реальной жизнью
Дабавлю, что:
1) Например, в С++ это требование выльется в то, чтобы всё находилось в одной единице трансляции ( т.е. в одном cpp файле ) — итого, БРЕД!
2) В .NET выльется в то, чтобы всё находилось в одной сборке — итого, БРЕД!
Вот ты говоришь "с успехом использовать". Не мог бы ты пояснить, где именно это можно с успехом использовать?
Здравствуйте, VladD2, Вы писали:
VD>Я и не спорю. Я о том, что реализация может быть не одна. А динамика от статики и отличается только тем, что в рантайме анализируеются какие-то данные.
VD>Просто забавно слышать что код типа if (a) ... не требует поддержки в рантайме. А a.b() требует, так как b виртуальная функция.
if запросто может не требовать. Если значение условия выводится из типа...
Здравствуйте, Кодт, Вы писали:
К>if запросто может не требовать. Если значение условия выводится из типа...
Их сам по себе выполняется в рантайме. Что при вызове виртуального метода, что при выполнении if-а производятся некие действия в рантайме. При этом никаких рантам-прдсистем не требуется. Это всего лишь код сгенерированный компилятором. Вот о чем я говорю.
... << RSDN@Home 1.1.4 beta 3 rev. 206>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.