Рассмотрим:
struct X
{
template <typename T>
T incr(const T& t)
{
return t + 1;
}
};
Поскольку incr()
применяется к различным типам T, генерируются новые функции.Скажем, внутри app.c++
у вас есть:
X x;
x.incr(7); // incr<int>()
x.incr(7.0); // incr<double>()
x.incr("hello"); // incr<const char*>()
Затем, когда он компилирует app.c++
, он видит 3 функции, которые - если бы incr
было разрешено быть virtual
- могли бы освободить место для трехприведенные выше экземпляры в таблице виртуальной диспетчеризации для X. Затем скажите, что она загружает общую библиотеку во время выполнения, и код для этой библиотеки имеет 2 экземпляра X::incr
для uint32_t
и std::string::const_iterator
.dlopen()
потребуется увеличить существующую таблицу виртуальной отправки для уже созданных объектов, чтобы освободить место для двух новых функций.Звучит не слишком ужасно, но учтите:
каждый бит кода, вызывающий виртуальные функции, должен знать, был ли адрес этих функций увеличен на некоторое смещение во время выполнения (из-задинамическая загрузка дополнительных экземпляров), поэтому в каждой виртуальной диспетчеризации требуется дополнительная память и затраты на производительность
, когда существует множественное наследование, или если сам производный класс является производным, компилятор может захотеть создатьединая таблица виртуальной диспетчеризации для всего набора виртуальных функций (одна опция, есть много для реализации виртуальной диспетчеризации): в этом случае новые виртуальные функции либо сместят виртуальные функции других классов, либо должны быть не связаны с существующими,Опять же, больше затрат времени выполнения в любой схеме, чтобы справиться с этим.
Таким образом, очень редкие случаи, когда это может быть полезно, не стоит идти на компромисс и усложнять более распространенный случайВиртуалы.