Подтвердить что ты не робот

Соответствие перегруженной функции ее полиморфному аргументу

Хорошо, название - глоток, и я думаю, что, вероятно, почему было трудно найти ответ через Google или этот сайт. Возможно, я не знаю, как правильно выражать проблему, но здесь говорится:

У меня есть ряд методов в классе SimpleOpenGLRenderer, которые принимают один аргумент, расширяющий класс Model. Таким образом, идея заключается в том, что в зависимости от типа модели рендерер будет вызывать правильный метод, который знает, как его отображать. Ниже приведен упрощенный пример исполняемого файла, основанный на проблеме:

#include <stdio.h>

class Model {};

class Cube : public Model {};

class Sphere : public Model {};

class Renderer
{
  public:
    virtual void renderModel(const Model& model) = 0;
};

class SimpleOpenGLRenderer
{
  public:
    void renderModel(const Cube& model)
    {
      printf("Render the cube.\n");
    }

    void renderModel(const Model& model)
    {
      printf("Throw an exception, my renderer does not support the model type you have provided.\n");
    }

    void renderModel(const Sphere& model)
    {
      printf("Render the sphere.\n");
    }
};

int
main(int argc, char** argv)
{
  Cube cube;
  Model& model = cube;
  SimpleOpenGLRenderer renderer;

  renderer.renderModel(cube);
  renderer.renderModel(model);
}

Результат из примера:

Render the cube.
Throw an exception, my renderer does not support the model type you have provided.

Для опытного разработчика на С++ может показаться очевидным, что это не работает так, как планировалось, но это просто не имеет смысла для меня. Во время выполнения я не буду знать точный тип Model, переданный рендереру (следовательно, попытка перегрузки для его устранения). Исходя из фона Java, я использовал эту технику до и в Java, метод, который будет вызван, будет таким, который наилучшим образом соответствует типу времени выполнения аргумента. В С++ он, похоже, соответствует типу ссылки на компиляцию, даже если эта ссылка может оказаться в подклассе, который, на мой взгляд, лучше соответствует другой функции.

До сих пор я использовал этот тип времени выполнения как должное. Это просто не существует в С++, или я об этом неправильно? Должен ли я делать что-то по-другому на С++, чтобы достичь этого?

Спасибо,

Гэри.

4b9b3361

Ответ 1

Перегрузки в С++ разрешаются во время компиляции на основе статического типа аргумента.

Там может быть полезен метод, известный как "двойная отправка":

class Model {
    virtual void dispatchRender(Renderer &r) const = 0;
};

class Cube : public Model {
    virtual void dispatchRender(Renderer &r) const {
        r.renderModel(*this); // type of "this" is const Cube*
};

int main() {
    Cube cube;
    Model &model = cube;
    SimpleOpenGLRenderer renderer;
    cube.dispatchRender(renderer);
}

Обратите внимание, что базовый класс Renderer должен содержать все перегрузки, которые выполняет SimpleOpenGLRenderer. Если вы хотите, чтобы это было специфично для SimpleOpenGLRenderer, какие существуют перегрузки, вы могли бы поместить функцию "Особая конкретная отправка" в Model или вы могли бы игнорировать эту технику и вместо этого использовать dynamic_cast несколько раз в SimpleOpenGLRenderer::renderModel для проверки типа.

Ответ 2

В вашем коде функции перегрузки разрешены на основе статического типа аргумента.

Вероятно, вам нужен механизм double-dispatch, который очень близок к шаблону посетителя. Прочтите эти данные:

Ответ 3

Для "перегрузки во время выполнения" на основе динамического типа можно использовать шаблон посетителя.

Ответ 4

Ваш код является хорошим кандидатом на совпадение типов выполнения, если вы его используете. Здесь вы получаете Cube в Model& и передаете то же самое просто на renderModel(). До сих пор у вас не было возможности компилятору использовать тип времени выполнения. Но скорее полагаясь на статический тип объекта.

Двумя способами вы могли бы использовать проверку типа времени выполнения. Один использует dynamic_cast<>, а другой - метод интерфейса в Model. то есть.

class Model {
  virtual void print () { printf("throw..."); }  // provide an interface method
};

class Cube : public Model {
  virtual void print () { print("Render cube\n"; }  // implement it
};

class Sphere : public Model {
  virtual void print () { print("Render sphere\n"; }  // implement it
};

class SimpleOpenGLRenderer
{
  public:
  void renderModel(const Model& model)
  {
    model.print();
  }
};

Ответ 5

В С++ разрешающая перегрузка для вызова выполняется во время компиляции.

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

Я думаю, что самый чистый способ сделать это - это то, что называется шаблон посетителя. Ваш SimpleOpenGLRenderer может вызвать метод model.renderOn( *this ). Тогда Model::renderOn представляет собой либо набор перегрузок, по одному для каждого возможного типа средства визуализации, либо это единственный виртуальный метод, который использует dynamic_cast для обнаружения типа средства визуализации. В любом случае он затем обращается к рендереру, но теперь этот вызов знает, какой тип рендерера он и какой тип сам, и может также вызвать очень конкретный метод рендеринга, например, SimpleOpenGLRenderer::renderCube.

Приветствия,

Ответ 6

Другие решения здесь будут делать именно то, что вы хотите. Но, на мой взгляд, ценой сложности. Если ваша проблема будет точно такой, как описано, я бы предложил изменить архитектуру вашего решения. Разве рендер не пытается выполнить работу модели? То, что я вижу, является выражением предложения с перегрузкой с перегрузкой.

Как сделать модели рендеринга самостоятельно, возможно, используя некоторый класс, предлагающий более примитивные методы рисования:

class Cube : public Model {
  render(RenderTool& tool) {
    tool.renderCube(); //or even more low level
  }
};

class SimpleOpenGLRenderer {
  public:
  RenderModel(Model& model) {
    model.render(renderTool);
  }
  private:
  SomeOpenGLRenderingTool renderTool;
};