純粋な抽象ベースと 2 つの派生クラスがあります。
struct B { virtual void foo() = 0; };
struct D1 : B { void foo() override { cout << "D1::foo()" << endl; } };
struct D2 : B { void foo() override { cout << "D1::foo()" << endl; } };
ポイント A の呼び出しfoo
は、非仮想メンバー関数の呼び出しと同じコストがかかりますか? それとも、D1 と D2 が B から派生していない場合よりもコストが高くなりますか?
int main() {
D1 d1; D2 d2;
std::vector<B*> v = { &d1, &d2 };
d1.foo(); d2.foo(); // Point A (polymorphism not necessary)
for(auto&& i : v) i->foo(); // Polymorphism necessary.
return 0;
}
回答: Andy Prowlの回答は一種の正しい回答です。gcc のアセンブリ出力を追加したかっただけです ( godboltでテスト: gcc-4.7 -O2 -march=native -std=c++11)。直接関数呼び出しのコストは次のとおりです。
mov rdi, rsp
call D1::foo()
mov rdi, rbp
call D2::foo()
ポリモーフィック呼び出しの場合:
mov rdi, QWORD PTR [rbx]
mov rax, QWORD PTR [rdi]
call [QWORD PTR [rax]]
mov rdi, QWORD PTR [rbx+8]
mov rax, QWORD PTR [rdi]
call [QWORD PTR [rax]]
ただし、オブジェクトが派生せずB
、直接呼び出しを実行するだけの場合、gcc は関数呼び出しをインライン化します。
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
これにより、さらに最適化が可能になる可能性があります。派生しない場合、D1
それらD2
は同等ではないとB
思います(少なくとも、これらの最適化を使用したこのバージョンの gcc では、-O3 はインライン化なしで同様の出力を生成しました)。およびが から派生する場合に、コンパイラがインライン化するのを妨げるものはありますか?D1
D2
B
「修正」:デリゲートを使用します (別名、仮想関数を自分で再実装します):
struct DG { // Delegate
std::function<void(void)> foo;
template<class C> DG(C&& c) { foo = [&](void){c.foo();}; }
};
次に、デリゲートのベクトルを作成します。
std::vector<DG> v = { d1, d2 };
これにより、非ポリモーフィックな方法でメソッドにアクセスする場合にインライン化が可能になります。std::function
ただし、ベクターへのアクセスは、単に仮想関数を使用するよりも遅くなる (または、型の消去に仮想関数を使用するため、少なくとも同じくらい高速になる) と思います (まだ Godbolt でテストできません)。