Проверка интерфейса.
Динамический полиморфизм заставляет ребенка уважать интерфейс.
Статический полиморфизм не заставит ребенка уважать интерфейс (пока вы действительно не вызвать функцию), поэтому, если вы не предоставите полезный метод, вы можете использовать непосредственно 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(); }
Второй подход не дает абсолютно никаких преимуществ по сравнению с второй. ;-) –
См. Преимущества и недостатки в Wiki: http://en.wikipedia.org/wiki/Template_metaprogramming#Static_polymorphism –
Вы пытаетесь понять, как перейти от интерфейсов Java к C++? – user2485710