Реализация интерфейса для статического полиморфизма
Как реализовать интерфейс так, чтобы в каждом классе должны были быть реализованы все функции из класса интерфейса?
Нашел такое решение:
- Программист, который хочет задать определенный интерфейс для всех дочерних классов, составляет список обязательных функций.
- Каждую из этих функций нужно якобы вызвать в базовом классе.
- Не зная ничего о реализации дочерних классов, это можно реализовать с помощью хитрости - вызываем функцию через указатель.
По идее, код не должен компилироваться, если в каком-то из дочерних классов нет реализации функции из списка. Но... оно компилируется.
Первый раз я расстроен, что мой код компилируется.
Помогите сломать компиляцию :-)
Есть небольшие требование: Не должно быть дополнительных вычислений в run-time
template <class AnyFruitType>
struct IFruit{
void _() {
static_cast<AnyFruitType*>(this)->foo_0();
static_cast<AnyFruitType*>(this)->foo_1();
static_cast<AnyFruitType*>(this)->foo_2();
}
};
// Наследуюсь от класса-интерфейса, полагая, что в нем произойдет проверка
struct apple : IFruit<apple>{
void foo_1(){}
void foo_2(){}
// void foo_3(){} делаем вид, что не реализована одна из функций
};
struct banana : IFruit<banana>{
void foo_1(){}
void foo_2(){}
void foo_3(){}
};
int main() {
apple a;
banana b;
a.foo_1();
b.foo_1();
//a.foo_3(); Код компилируется, если нет вызова нереализованной функции.
}
Ответы (1 шт):
Чтобы такая проверка в базовом классе заработала, проверяющую функцию необходимо инстанцировать. Чтобы не было бессмысленных вызовов функций необходимо брать указатели на них. Зато имеет смысл делать проверку в конструкторе, чтобы он инстанцировался при генерации конструктора класса-наследкика.
template<typename AnyFruitType>
struct IFruit
{
constexpr IFruit(void) noexcept
{
static_cast<void>(&AnyFruitType::foo_1);
static_cast<void>(&AnyFruitType::foo_2);
static_cast<void>(&AnyFruitType::foo_3);
}
};