C ++ Двойная диспетчеризация с полиморфизмом во время выполнения? - PullRequest
0 голосов
/ 17 февраля 2019

Можно ли выполнить двойную диспетчеризацию с полиморфизмом во время выполнения?

Скажем, у меня есть несколько классов, и некоторые из этих классов можно добавить / умножить / и т. Д., И я хочу сохранить их динамически в другом классекоторый выполняет стирание типа во время выполнения.Затем скажите, что я хочу выполнить основные операции с данными, содержащимися в этом классе.

Способ справиться с этим (насколько я знаю) - использовать двойную диспетчеризацию для специализации операции.Тем не менее, все решения, с которыми я столкнулся, основаны на том факте, что у вас есть значительное количество типов, а затем вы используете виртуальные вызовы функций или dynamic_cast s, if-else и RTTI для определения типа во время выполнения.Поскольку данные, хранящиеся в классе, не известны до времени выполнения, я не могу создать кучу виртуальных методов или выполнить проверку методом «грубой силы» для типов.Поэтому я подумал, что шаблон посетителя будет лучшим решением, но даже тогда я не могу понять, возможно ли это.

У меня есть класс-обертка, который содержит умный указатель на вложенный полиморфкласс для реализации типа стирания и полиморфизма во время выполнения, но я не могу понять, возможно ли использовать шаблон посетителя для двойной отправки этого.

Обратите внимание, что приведенный ниже код неполон, он просто показывает моймыслительный процесс.

class Wrapper {
private:
  class Concept;
  template<typename T> class Model;

  class BaseVisitor {
  public:
    virtual ~Visitor() = default;
    virtual void visit(Concept &) = 0;
  };

  template<typename T>
  class Visitor : public BaseVisitor {
  private:
    T first_;
  public:
    Visitor(T first) : first_(first) {}

    virtual void visit(Concept &other) override {
      // perform addition
    }
  };

  class Concept {
  public:
    virtual ~Concept() = default;

    virtual void add(Concept &m) const = 0;
    virtual void accept(BaseVisitor &visitor) const = 0;
  };

  template<typename T>
  class Model final : public Concept {
  private:
    T data_;

  public:
    Model(T m)
        : data_(m) {}

    virtual void add(Concept &m) const override {
      Visitor<T> v(data_);
      m.accept(v);
    };
    virtual void accept(BaseVisitor &visitor) const override {
      visitor.visit(*this);
    };
  };

  std::shared_ptr<const Concept> ptr_;

  // This isn't right, it just illustrates what I'm trying to do.
  // friend Something operator+(Wrapper &lhs, Wrapper &rhs) {
  //   return (*lhs.ptr_).add(*rhs.ptr_);
  // }

public:
  template<typename T>
  Wrapper(T value) : ptr_(std::make_shared<Model<T>>(value)) {}
};

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

Возможно ли это вообще?


РЕДАКТИРОВАТЬ

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


Чтобы быть еще более конкретным, я работаю с классами для выполнения отложенных вычислений и символических вычислений, то есть у меня есть классы, такие как Number<T>, которые могут бытьNumber<int>, Number<double> и т. Д. И классы, такие как Variable, Complex<T> и все комбинации TMP для различных операций, таких как Add<Mul<Variable, Variable>, Number<double>> и т. Д.

Я могу работать со всемиэто нормально во время компиляции, но тогда мне нужно иметь возможность хранить их в векторе - что-то вроде std::vector<Wrapper> x = {Number<int>, Variable, Add<Number<double>, Variable>};.Моим лучшим предположением при этом было выполнение стирания типов для хранения выражений внутри полиморфного Wrapper.Это служит двойной обязанностью для поддержки поддержки синтаксического анализа во время выполнения символических выражений.

Однако функции, которые я написал для обработки добавления, такие как

template<typename T1, typename T2>
const Add<T1, T2> operator+(const T1 &lhs, const T2 &rhs)
{ return Add<T1, T2>(lhs, rhs); }

, не могут принять Wrapper ивытащите тип (из-за стирания типа).Однако я могу вставить Wrapper в класс выражений Add, что означает, что я могу носить скрытые типы.Проблема в том, что когда я приступаю к оценке результата, например, Add<Wrapper, Wrapper>.Чтобы узнать, к чему это приведет, мне нужно выяснить, что на самом деле внутри, или сделать что-то по принципу двойной отправки.

Основная проблема заключается в том, что примеры для двойной отправки, наиболее точно соответствующие моей проблеме, , как этот вопрос для SO , основаны на том факте, что я могу выписать все классы, такие каккак Shapes, Rectangles.Поскольку я не могу сделать это явно, мне интересно, есть ли способ выполнить двойную диспетчеризацию для оценки выражения на основе данных, содержащихся в классе Model<T> выше.

...