Допустим, у нас есть простой класс с виртуальным методом:
class A {
public:
virtual ~A() = default;
virtual void f(void *ptr) = 0;
};
Подразумевается, что разные реализации A принимают в методе f указатели на разные объекты и как-то их обрабатывают. То есть, типичная реализация метода f такая:
void B::f(void *ptr) {
auto data = (SomeType*) ptr; // SomeType разный для разных наследников A
//делаем что-нибудь с data
}
Допустим, я хочу избавиться от необходимости кастовать ptr в конкретный тип в каждом наследнике класса A (например, потому что методов подобных f очень много и наследников очень много).
И я делаю что-то вроде такого:
template<typename T> class ATemplate: public A {
protected:
virtual void realF(T *t) = 0;
public:
void f(void *ptr) final {
realF(static_cast<T*>(ptr));
}
};
Теперь можно наследоваться не от A, а от ATemplate, передать один раз желаемый тип в шаблон и самому руками уже не надо ничего кастовать (функций подобных f может быть много, но все работают с одним и тем же типом в рамках одной и той же реализации A).
Однако если посмотреть на результат компиляции, то теперь у нас при каждом вызове f происходит два косвенных перехода - сначала при вызове f (ведь это виртуальная функция), а затем при вызове realF (ведь это тоже виртуальная функция). А хотелось бы обойтись одним переходом. Ведь на самом деле можно было бы подложить адрес realF на место f в таблице виртуальных методов (кастование указателя это лишь синтаксический сахар и на уровне ассемблерного кода не создаёт инструкции, поскольку это лишь вопрос интерпритации указателя дальше по коду, а сам указатель не меняется).
В Java есть type erasure и поэтому можно было написать просто:
class A<T> {
public abstract void f(T arg);
};
class B<SomeType> {
public void f(SomeType arg) {
// что-то делаем
}
};
В C++ мы не можем сделать базовый класс (A) шаблонным, иначе сломается runtime полиморфизм. Чтобы и не надо было делать явный каст в наследниках, и полиморфизм работал, пришлось сделать костыль с промежуточным шаблоном-наследником, но он приводит к генерации неоптимального кода.
Какие ещё есть варианты повторить то, что можно сделать в Java?