Ada perbedaan yang cukup besar ketika Anda memiliki template dan mulai mengambil kelas dasar sebagai parameter template:
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);
}
Bagian yang menyenangkan dari itu adalah bahwa Anda sekarang dapat mendefinisikan fungsi antarmuka dan non-antarmuka nanti untuk mendefinisikan kelas. Itu berguna untuk antarmuka antar kerja antar pustaka (jangan mengandalkan ini sebagai proses desain standar pustaka tunggal ). Anda tidak perlu mengeluarkan biaya apa pun untuk semua kelas Anda - Anda mungkin typedef
akan mendapatkan sesuatu jika Anda mau.
Perhatikan bahwa, jika Anda melakukan ini, Anda mungkin juga ingin mendeklarasikan konstruktor salin / pindahkan sebagai templat: memungkinkan untuk membuat dari antarmuka yang berbeda memungkinkan Anda untuk 'melemparkan' di antara berbagai B<>
jenis.
Ini dipertanyakan apakah Anda harus menambahkan dukungan untuk const A&
di t_hello()
. Alasan yang biasa untuk penulisan ulang ini adalah untuk beralih dari spesialisasi berbasis warisan ke yang berbasis template, sebagian besar karena alasan kinerja. Jika Anda terus mendukung antarmuka lama, Anda hampir tidak dapat mendeteksi (atau menghalangi) penggunaan lama.