Es gibt einen erheblichen Unterschied, wenn Sie Vorlagen haben und Basisklassen als Vorlagenparameter verwenden:
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);
}
Der lustige Teil davon ist, dass Sie jetzt Schnittstellen- und Nicht-Schnittstellenfunktionen definieren können, um später Klassen zu definieren. Dies ist nützlich für das Interworking von Schnittstellen zwischen Bibliotheken (verlassen Sie sich nicht darauf als Standardentwurfsprozess einer einzelnen Bibliothek). Es kostet Sie nichts, dies für alle Ihre Klassen zuzulassen - Sie könnten sogar typedef
B zu etwas, wenn Sie möchten.
Beachten Sie, dass Sie in diesem Fall möglicherweise auch Kopier- / Verschiebungskonstruktoren als Vorlagen deklarieren möchten: Wenn Sie über verschiedene Schnittstellen konstruieren können, können Sie zwischen verschiedenen B<>
Typen wechseln .
Es ist fraglich, ob Sie Unterstützung für const A&
in hinzufügen sollten t_hello()
. Der übliche Grund für dieses Umschreiben besteht darin, von einer vererbungsbasierten Spezialisierung zu einer vorlagenbasierten zu wechseln, hauptsächlich aus Leistungsgründen. Wenn Sie die alte Benutzeroberfläche weiterhin unterstützen, können Sie die alte Nutzung kaum erkennen (oder davon abhalten).