2

и Рождеством всех!Имеет ли смысл статический полиморфизм для реализации интерфейса?

Я изучаю статический полиморфизм, и я читаю превосходную книгу Андрея Александреску о разработке политики. В моем коде я натолкнулся на следующее: у меня есть интерфейс Interface, который указывает, что должен присутствовать метод Foo. Этот интерфейс будет реализован классом Impl. У меня есть следующие два варианта:

1) Динамический полиморфизм

class Interface { 
public: 
    virtual void Foo() = 0; 
} 

class Impl : public Interface { 
public: 
    void Foo() {}; 
} 

2) Статический полиморфизм

class Impl { 
{ 
public: 
    void Foo() {}; 
} 

template <class I> 
class Interface : public I 
{ 
public: 
    void Foo() { I::Foo(); } //not actually needed 
} 

ли смысл использовать статический полиморфизм в этом случае? Предоставляет ли второй подход какие-либо преимущества по сравнению с первым? Интерфейс указывает только на наличие некоторых методов, и его механика одинакова для различной реализации - так что не совсем так, как в случае, описанном в книге, поэтому я чувствую, что могу быть слишком усложняющим.

Обновление: Мне не нужно полиморфное поведение во время выполнения; правильная реализация известна во время компиляции.

+0

Второй подход не дает абсолютно никаких преимуществ по сравнению с второй. ;-) –

+0

См. Преимущества и недостатки в Wiki: http://en.wikipedia.org/wiki/Template_metaprogramming#Static_polymorphism –

+0

Вы пытаетесь понять, как перейти от интерфейсов Java к C++? – user2485710

ответ

3

Проверка интерфейса.

Динамический полиморфизм заставляет ребенка уважать интерфейс.

Статический полиморфизм не заставит ребенка уважать интерфейс (пока вы действительно не вызвать функцию), поэтому, если вы не предоставите полезный метод, вы можете использовать непосредственно Impl.

class InvalidImpl {}; // Doesn't respect interface. 
void bar() 
{ 
    InvalidImpl invalid; 

    // this compiles, as not "expected" since InvalidImpl doesn't respect Interface. 
    CRTP_Interface<InvalidImpl> crtp_invalid; 

#if 0 // Any lines of following compile as expected. 
    invalid.Foo(); 
    crtp_invalid.Foo(); 
#endif 
} 

У вас есть 3-й путь, используя черты, чтобы проверить, что класс верифицировать интерфейс:

#include <cstdint> 
#include <type_traits> 

// Helper macro to create traits class to know if class has a member method 
#define HAS_MEM_FUNC(name, Prototype, func)        \ 
    template<typename U>            \ 
    struct name {              \ 
     typedef std::uint8_t yes;          \ 
     typedef std::uint16_t no;          \ 
     template <typename T, T> struct type_check;      \ 
     template <typename T = U>          \ 
     static yes &chk(type_check<Prototype, &T::func> *);    \ 
     template <typename > static no &chk(...);      \ 
     static constexpr bool value = sizeof(chk<U>(0)) == sizeof(yes); \ 
    } 

// Create traits has_Foo. 
HAS_MEM_FUNC(has_Foo, void (T::*)(), Foo); 

// Aggregate all requirements for Interface 
template <typename T> 
struct check_Interface : 
    std::integral_constant<bool, has_Foo<T>::value /* && has_otherMethod<T>::value */> 
{}; 

// Helper macros to assert if class does respect interface or not. 
#define CHECK_INTERFACE(T) static_assert(check_Interface<T>::value, #T " doesn't respect the interface") 
#define CHECK_NOT_INTERFACE(T) static_assert(!check_Interface<T>::value, #T " does respect the interface") 

Позволяет проверить:

class Interface { 
public: 
    virtual void Foo() = 0; 
}; 

class Child_Impl final : public Interface { 
public: 
    void Foo() override {}; 
}; 

#if 0 // Following doesn't compile as expected. 
class Child_InvalidImpl final : public Interface {}; 
#endif 

template <class I> 
class CRTP_Interface : public I 
{ 
public: 
    void Foo() { I::Foo(); } // not actually needed 
}; 

class Impl { public: void Foo(); }; // Do respect interface. 
class InvalidImpl {};    // Doesn't respect interface. 

CHECK_INTERFACE(Interface); 
CHECK_INTERFACE(Child_Impl); 
CHECK_INTERFACE(Impl); 
CHECK_INTERFACE(CRTP_Interface<Impl>); 

CHECK_NOT_INTERFACE(InvalidImpl); 
CHECK_INTERFACE(CRTP_Interface<InvalidImpl>); // CRTP_Interface<T> _HAS_ Foo (which cannot be invoked) 

Производительность

С Dynamic полиморфизма, вы можете оплатить виртуальный вызов. Вы можете уменьшить виртуальный вызов, добавив final как class Child final : public Interface.

Так компилятор может оптимизировать код, как:

void bar(Child& child) { child.Foo(); } // may call Child::Foo not virtually. 

, но он не может сделать никакой магия (предполагается, что bar не встраиваемая) с:

void bar(Interface& child) { child.Foo(); } // have to virtual call Foo. 

Теперь предположит, что в интерфейсе у вас есть :

void Interface::Bar() { /* some code */ Foo(); } 

Мы находимся во втором случае, когда у нас есть виртуальный вызов Foo.

Статический полиморфизм решает, что с помощью:

template<class Derived> 
void Interface<Derived>::Bar() { /* some code */ static_cast<Derived*>(this)->Foo(); } 
1

Имеет ли смысл использовать статический полиморфизм, зависит от того, как вы используете этот класс.

Виртуальные функции вводят уровень косвенности. Виртуальные функции позволяют вызывать метод в производном классе с помощью указателя или ссылки на объект базового класса (который является общим для всех производных классов).

Статический полиморфизм не использует общий базовый класс. Каждый производный класс использует собственный базовый класс. Эти базовые классы часто создаются из общего шаблона класса. Тем не менее, это разные классы. Это приводит к таким вещам, которые, например, указатели или ссылки на такие объекты не могут быть сохранены в общем контейнере.

+0

В моем примере класс интерфейса - это производный класс (для статического примера). В обоих случаях я могу вызвать 'Interface.Foo()'. –

+0

@DanNestor No. В вашем примере вы будете иметь интерфейс .Foo() 'и' интерфейс .Foo() 'but' Interface 'и' Interface '- это два разных класса. Вы не можете хранить 'Интерфейс ' в 'std :: vector >' и наоборот. – Johan

+0

О, я понимаю, спасибо. Тем не менее, мне не нужно полиморфное поведение во время выполнения, я обновлю вопрос. –