Існує значна різниця, коли у вас є шаблони і починаєте використовувати базовий клас (и) як параметри (и) шаблону:
struct None {};
template<typename... Interfaces>
struct B : public Interfaces
{
void hello() { ... }
};
struct A {
virtual void hello() = 0;
};
template<typename... Interfaces>
void t_hello(const B<Interfaces...>& b) // different code generated for each set of interfaces (a vtable-based clever compiler might reduce this to 2); both t_hello and b.hello() might be inlined properly
{
b.hello(); // indirect, non-virtual call
}
void hello(const A& a)
{
a.hello(); // Indirect virtual call, inlining is impossible in general
}
int main()
{
B<None> b; // Ok, no vtable generated, empty base class optimization works, sizeof(b) == 1 usually
B<None>* pb = &b;
B<None>& rb = b;
b.hello(); // direct call
pb->hello(); // pb-relative non-virtual call (1 redirection)
rb->hello(); // non-virtual call (1 redirection unless optimized out)
t_hello(b); // works as expected, one redirection
// hello(b); // compile-time error
B<A> ba; // Ok, vtable generated, sizeof(b) >= sizeof(void*)
B<None>* pba = &ba;
B<None>& rba = ba;
ba.hello(); // still can be a direct call, exact type of ba is deducible
pba->hello(); // pba-relative virtual call (usually 3 redirections)
rba->hello(); // rba-relative virtual call (usually 3 redirections unless optimized out to 2)
//t_hello(b); // compile-time error (unless you add support for const A& in t_hello as well)
hello(ba);
}
Найцікавіше, що тепер ви можете визначити функції інтерфейсу та неінтерфейсу пізніше для визначення класів. Це корисно для взаємодії інтерфейсів між бібліотеками (не покладайтеся на це як на стандартний процес проектування однієї бібліотеки). Вам нічого не коштує, щоб дозволити це для всіх ваших занять - ви навіть можете typedef
B на щось, якщо хочете.
Зауважте, що якщо ви це зробите, ви, можливо, захочете також оголосити конструктори копіювання / переміщення як шаблони: дозволяючи створювати з різних інтерфейсів, ви можете "робити" між різними B<>
типами.
Сумнівно, чи варто додавати підтримку const A&
в t_hello()
. Звичайна причина цього переписування - перехід від спеціалізації, заснованої на спадщині, до шаблону, в основному з міркувань продуктивності. Якщо ви продовжуєте підтримувати старий інтерфейс, ви навряд чи зможете виявити (або відмовитися від) старого використання.