2017-01-14 13 views
5

Załóżmy, że mam następujące definicje klasyWarunkowo dziedziczą z klasy bazowej czysty

struct base { 
    virtual int f() = 0; 
}; 

struct A: public base { 
    int f() final { return 1; } 
}; 

struct B: public base { 
    int f() final { return 2; } 
}; 

Czy to możliwe, aby włączyć A i B do szablonów, które mają parametr bool która określa, czy dziedziczyć z base czy nie? Mam przypadki użycia, które wymagają lub nie wymagają klasy bazowej zapewniającej wspólny interfejs.

Załóżmy, że A i B mają wiele funkcji składowych, więc powielanie implementacji byłoby uciążliwe. Ale sizeof(A) i sizeof(B) są małe.

+0

Potrzebujesz dwóch wersji? Zakładając, że 'base' zawiera tylko funkcje abstrakcyjne, nie powinno być żadnych kosztów runtime. W twoim przykładzie twój optymalizator powinien zobaczyć, że 'A' użyje' f() 'stamtąd, ponieważ jest' końcowe'. – Guvante

+0

@ Guwane: Sprawia, że ​​typ jest polimorficzny, co daje mu vtable. Nie koszt runtime, ale koszt niemniej jednak. – ildjarn

+0

@Guvante Wielkość typu dziedziczenia jest większa o wielkość wskaźnika, który wskazuje na tabelę vtable. – SU3

Odpowiedz

3

Wpadłem na bardziej bezpośrednie podejście, którego szukałem, bez powielania kodu.

struct base { 
    virtual int f() = 0; 
}; 

struct empty_base { }; 

template <bool Inherit> 
struct A final: public std::conditional_t<Inherit,base,empty_base> { 
    int f() { return 1; } 
}; 
4

Sure:

template <bool> struct A 
{ 
    // ... 
}; 

template <> struct A<true> : base 
{ 
    // ... 
}; 

(Zauważ, że można zrobić A<true> pochodzą z A<false> jeśli unika redundancji).

Na przykład:

template <bool> struct A 
{ 
    void f() { std::cout << "A::f called\n"; } 
}; 

template <> struct A<true> : A<false>, base 
{ 
    void f() override { A<false>::f(); } 
}; 

int main() 
{ 
    A<false> a1; 
    A<true> a2; 
    a1.f(); 
    a2.f(); 
    static_cast<base&>(a2).f(); 
} 
+0

Chociaż nie pozwoliłoby to na 'A a; podstawa * p = & a; p-> f(); 'działa poprawnie. Przypuszczam, że stanie się tak tylko wtedy, gdy 'base :: f()' nie jest abstrakcyjne (problem z diamentem). Czy twoja implementacja sprawia, że ​​wywołanie 'p-> f()' jest wolniejsze niż w mojej oryginalnej implementacji? – SU3

+0

Jeśli istnieje sposób na zrobienie 'A :: f()' 'final'? – SU3

+1

Musisz skopiować deklarację (i zadzwonić do drugiej poprzez 'A :: f()'). Również jeśli umieścisz 'f()' w 'A ', to nie spełni wymagania 'base :: f()' będącego abstrakcją. – Guvante

1

Ponieważ używasz czystego bazy klasa rozróżnienie nie powinno być ważne, ponieważ optymalizator uniknie wywołania funkcji wirtualnej, gdy zadzwonisz pod numer A::f(), ponieważ tam Nigdy nie będę klasą pochodną, ​​która implementuje inną wersję f().

Możesz także wykonać class A final : base, jeśli nie planujesz dziedziczyć po A, aby uniknąć konieczności dodawania final do każdej funkcji.

+0

Dzięki. Zapomniałem, że możesz wcisnąć 'final' na zajęciach zamiast funkcji. – SU3

+0

Możesz także zrobić "klasa A zapieczętowana: publiczna baza {...};' –

+0

Co to jest 'zapieczętowana'? Czy to jest rozszerzenie Microsoft? – SU3