Поясните, пожалуйста, почему нельзя создать виртуальную шаблонную функцию?
Нашел следующее объяснение
Member function templates cannot be declared virtual. This constraint
is imposed because the usual implementation of the virtual function
call mechanism uses a fixed-size table with one entry per virtual
function. However, the number of instantiations of a member function
template is not fixed until the entire program has been translated.
Hence, supporting virtual member function templates would require
support for a whole new kind of mechanism in C++ compilers and
linkers. In contrast, the ordinary members of class templates can be
virtual because their number is fixed when a class is instantiated
То есть теоретически такой функционал реализовать можно, но это потребует серьезного изменения принципов работы существующих компилятор и линковщиков. Или есть и другие причины?
Ответ
Простой ответ:
В С++ шаблон функции не является функцией, поэтому шаблон не может быть виртуальным.
В C#/Java/etc используются не шаблоны, а generics. Generic-функция это (одна) функция, поэтому там такой проблемы нет.
Сложный ответ:
В С++ виртуальные функции сделаны так, что их количество прописано в определении базового класса.
Это позволяет присвоить функции некоторый индекс в базовом классе и быстро находить ее по этому индексу.
struct Base {
func_t* vft; // скрытый член класса - массив виртуальных функций
virtual void f();
};
Base* x = new Derived;
x->f();
// компилируется в
x->vft[0]();
Если шаблоны будут виртуальными, то вместо перечисления функций в базовом классе надо искать все подстановки шаблона при вызовах функции.
Для этого вместо индексов надо использовать имена, и искать эти имена в хеш-таблице.
struct Base {
hash_map
Base* x = new Derived;
x->f
Скорость вызова значительно упадет, т.к. надо будет разрешать коллизии.
Можно использовать идеальную хеш-функцию (без коллизий).
struct Base {
func_t* vft; // скрытый член класса - массив (sic!) виртуальных функций
virtual void f();
};
Base* x = new Derived;
x->f();
// компилируется в
x->vft[ideal_hash("f
Но из-за динамической линковки (.so/.dll) весь исходный код программы недоступен, и при каждой загрузке SO/DLL надо останавливать всю программу, менять хеш-функцию и перестраивать все таблицы, чтобы учитывались типы, которые добавились в этой SO/DLL.
Использование JIT-компилятора может заменять виртуальные вызовы на обычные, и тогда никаких проблем с производительностью вызова не будет.
// вместо x->f(); генерируется
x->Derived::f();
// если доказано что тут может быть только Derived
Но девиртуализация работает только если количество классов мало, и на данный момент эффективных JIT-компиляторов нет. (Те что есть, например в LLVM, не показывают хороших результатов.)
Комментариев нет:
Отправить комментарий