Статический интерфейс - CRTP? Mixin? Другой? - PullRequest
1 голос
/ 19 июня 2019

Я заинтересован в реализации интерфейса.Я знаю, что стандартный способ сделать это - кодировать класс, имеющий чисто виртуальные методы, которые будут использоваться в качестве интерфейса, например:

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

, и использовать его:

class Implementation : public Interface {
public:
  virtual void doSomething() override { ... }
}

Или даже лучше применить NVI (не виртуальный интерфейс).

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

Поэтому я сосредоточился на «статическом полиморфизме» и попытался использовать CRTP для реализации интерфейсов:

template<typename T>
class Interface {
public:
  void doSomething() {
    static_cast<T&>(*this)._doSomething();
}

class Implementation : public Interface<Implementation> {
public:
  void _doSomething() { /* Do implementation specific stuff here */ }
}

class Implmentation2 : public Interface<Implementation2> {...}

Пока все хорошо.Но я вижу одну большую проблему.Когда я хочу сохранить несколько указателей на интерфейс какого-либо контейнера и получить доступ к экземплярам различных классов реализации, я сталкиваюсь с проблемой: Interface<T> всегда является отдельным классом, а не единственным интерфейсом:

Interface<Implementation> и Interface<Implementation2> - это разные типы.

Хорошо, давайте создадим общий базовый класс для получения интерфейсов из ...

template<typename T>
class Interface : public IfaceBase {
public:
  void doSomething() {
    static_cast<T&>(*this)._doSomething();
}

... но тогда яне может получить доступ к конечному экземпляру с помощью интерфейса.Мне кажется, я пытаюсь создать интерфейс для интерфейса, который звучит безумно само по себе в любом случае ...

Так что я обнаружил, что CRTP не слишком пригоден для использования в этом случае.Я искал в Интернете и нашел MIXIN, который, кажется, "CRTP перевернулся".Но я не уверен, что это можно использовать для моих целей ...

Не могли бы вы мне помочь?Если есть способ применения MIXINS или любой другой идиомы / любой другой, чтобы иметь интерфейсы C ++ без виртуалов, пожалуйста, поделитесь идеей:)

Заранее большое спасибо всем, кто хочет помочь!Ура Мартин

1 Ответ

0 голосов
/ 21 июня 2019

Я знаю два способа вызова не виртуальных методов из разных типов полиморфным способом:

  1. Стирание типа: оборачивает не виртуальные классы в другие, имеющие виртуальные методы (это относительно близко к тому, что вы пытались).
  2. Шаблон посетителя применяется к variant типам. Этот требует, чтобы все типы были известны во время компиляции, где вызывается метод.

Поскольку вам вообще не нужны виртуальные методы, я объясню только решение 2. Если вас больше интересует стирание типов, объяснено C ++ 'Стирание типов' (блог Дэйва Килиана) хорошее чтение.

Шаблон посетителей и типы вариантов (C ++ 17)

Предположим, у вас есть типы с не виртуальными методами:

class Implementation1 {
    void doSomething() { /* ... */ }
};

class Implementation2 {
    void doSomething() { /* ... */ }
};

Вы можете хранить любое их сочетание в контейнере, используя std :: option :

// #include <variant>
// #include <vector>
using ImplVariant = std::variant<Implementation1,Implementation2>;
std::vector<ImplVariant> array = {Implementation2(), Implementation1() /*, ...*/};

Чтобы вызвать doSomething на MyVariant объекте, вы применяете посетителя к нему. Это просто делается в C ++ 17, передавая универсальную лямбду в std :: visit :

for (auto& variant : array) {
    std::visit([](auto&& object) { object.doSomething(); }, variant);
}

Демонстрационная версия

Кроме того, вы можете сделать variant (умных) указателей.

...