Ile kosztuje wywołanie funkcji wirtualnej w sposób nie polimorficzny?

Mam czystą abstrakcyjną bazę i dwie klasy pochodne:

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; } };

Czy dzwonifoo w punkcie A koszt jest taki sam jak wywołanie do nie-wirtualnej funkcji członka? Czy może jest droższy niż gdyby D1 i D2 nie pochodziłyby z 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;
}

Odpowiedź: odpowiedźAndy Prowl jest właściwą odpowiedzią, chciałem tylko dodać wyjście zestawu gcc (testowane wGodbolt: gcc-4.7 -O2 -march = native -std = c ++ 11). Koszt bezpośrednich wywołań funkcji to:

mov rdi, rsp
call    D1::foo()
mov rdi, rbp
call    D2::foo()

A dla połączeń polimorficznych:

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]]

Jeśli jednak obiekty nie pochodzą zB i po prostu wykonujesz bezpośrednie połączenie, gcc będziew linii wywołanie funkcji:

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*)

Tomógłby włącz dalsze optymalizacje, jeśliD1 iD2 nie pochodzą zB więc chyba taknie, nie są równoważne (przynajmniej dla tej wersji gcc z tymi optymalizacjami -O3 wygenerował podobny wynik bez wstawiania). Czy jest coś, co uniemożliwia kompilatorowi umieszczenie w tym przypadkuD1 iD2 czerpać zB?

"Naprawić": używaj delegatów (samodzielnie reimplementuj funkcje wirtualne):

struct DG { // Delegate
 std::function<void(void)> foo;
 template<class C> DG(C&& c) { foo = [&](void){c.foo();}; }
};

a następnie utwórz wektor delegatów:

std::vector<DG> v = { d1, d2 };

pozwala to na wstawianie, jeśli masz dostęp do metod w sposób nie polimorficzny. Jednak myślę, że dostęp do wektora będzie wolniejszy (lub przynajmniej tak szybki, ponieważstd::function używa funkcji wirtualnych do kasowania typów) niż tylko do korzystania z funkcji wirtualnych (nie można jeszcze przetestować za pomocą godbolt).

questionAnswers(2)

yourAnswerToTheQuestion