使用virtual function的時候,每個class會多一個vtable儲存virtual function的
pointer:
class Interface {
public:
virtual void f() = 0;
virtual ~Interface() = default;
int a;
};
class Implement: Interface {
public:
void f() override {}
int b;
};
通常的記憶體分布會長這樣:
0 1 2 3 4 5 6 7 8
0┌───────────────┐
│ &Implement::f │
8├───────────────┤
│ &Interface::~Interface │
16├───────┬───────┤
│ a │ b │
24└───────┴───────┘
在程式執行時要呼叫f的時候就是從vtable中拿到f的function位址來呼叫的,這就代
表有一次indirect call的時間成本。
return (*vtable[0])();
使用variant儲存多種物件時,會像union一樣占用最大type所占用的空間+一個標示
type用的int。
template <typename Derived>
class Interface {
public:
void f() {static_cast<Derived*>(this)->f();}
int a;
};
class Implement: Interface<Implement> {
public:
void f() {}
int b;
};
std::variant<Implement> V;
而variant儲存一個Implement時候的記憶體分布會長這樣:
0 1 2 3 4 5 6 7 8
0┌───────┬───────┐
│ type_index │ a │
8├───────┼───────┘
│ b │
16└───────┘
其中type_index是代表目前內部儲存的type,-1是錯誤值,0是type list內第一個type。
使用visitor時,產生的程式碼會像這樣:
switch (type_index) {
case 0:
return visitor(*reinterpret_cast<Interface*>(&storage));
default:
throw std::bad_variant_access();
}
這裡執行時的成本為一或多個condition jump,與一個jump。總體所需時間略低於一個
indirection jump,而且還能受益於branch prediction,所以這裡使用variant會比使用
virtual function快得多。