มีความแตกต่างอย่างมากเมื่อคุณมีแม่แบบและเริ่มรับคลาสฐานเป็นพารามิเตอร์แม่แบบ:
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()
หรือไม่ เหตุผลปกติสำหรับการเขียนซ้ำนี้คือการย้ายออกจากความเชี่ยวชาญที่สืบทอดมาเป็นหนึ่งในเทมเพลตซึ่งส่วนใหญ่เป็นเหตุผลด้านประสิทธิภาพ หากคุณยังคงสนับสนุนอินเทอร์เฟซเก่าคุณสามารถตรวจหา (หรือยับยั้ง) การใช้งานเก่าได้ยาก