2017-01-14 4 views
5

Предположим, что у меня есть следующие определения классовУсловно наследуют от чистого базового класса

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

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

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

Можно ли превратить A и B в шаблоны, которые принимают параметр bool, который определяет, следует ли наследовать от base или нет? У меня есть случаи использования, которые выполняют или не требуют базового класса, обеспечивающего общий интерфейс.

Предположим, что A и B имеют множество функций-членов, поэтому дублирование реализации было бы утомительным. Но sizeof(A) и sizeof(B) являются маленькими.

+0

Вам нужны две версии? Предполагая, что 'base' содержит только абстрактные функции, не должно быть никаких затрат времени исполнения. В вашем примере ваш оптимизатор должен видеть, что любой 'A' будет использовать' f() 'оттуда, так как это' final'. – Guvante

+0

@Guvante: он делает тип полиморфным, что дает ему vtable. Не стоимость исполнения, но тем не менее стоимость. – ildjarn

+0

@Guvante Размер наследуемого типа больше размера указателя, который указывает на таблицу vtable. – SU3

ответ

3

Я придумал более прямой подход, который я искал, без дублирования кода.

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 
{ 
    // ... 
}; 

(Обратите внимание, что вы могли бы сделать A<true> извлечь из A<false>, если это позволяет избежать избыточности.)

Например:

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

Я, хотя это не допустимо для 'A a; base * p = & a; p-> f(); 'правильно работать. Я предполагаю, что это произойдет только в том случае, если 'base :: f()' не был абстрактным (проблема с алмазом). Ваша реализация делает вызов 'p-> f()' медленнее, чем в моей первоначальной реализации? – SU3

+0

Если есть способ сделать 'A :: f()' 'final'? – SU3

+1

Вам нужно будет скопировать объявление (и вызвать другое через 'A :: f()'). Кроме того, если вы поместите 'f()' в 'A ', это не будет удовлетворять абстрактному требованию 'base :: f()'. – Guvante

1

Поскольку вы используете чистую базу класс не должен быть важен, так как ваш оптимизатор избежит вызова виртуальной функции, когда вы вызываете A::f(), так как там wi ll никогда не будет производным классом, который реализует другую версию f().

Также вы можете сделать class A final : base, если вы не планируете наследовать от A, чтобы избежать необходимости добавлять final к каждой функции.

+0

Спасибо. Я забыл, что вы можете использовать 'final' в классе вместо функций. – SU3

+0

Вы также можете сделать «класс A запечатанный: общедоступная база {...};' –

+0

Что такое «запечатан»? Это расширение Microsoft? – SU3

Смежные вопросы