Соответствие перегруженной функции ее полиморфному аргументу - PullRequest
17 голосов
/ 01 августа 2011

Хорошо, заголовок довольно полный, и я думаю, что, возможно, поэтому было сложно найти ответ через 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.

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

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

Спасибо,

Гари.

Ответы [ 6 ]

17 голосов
/ 01 августа 2011

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

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

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 функцию диспетчеризации, специфичную для Simple, или вы можете игнорировать эту технику и вместо этого постоянно использовать dynamic_cast в SimpleOpenGLRenderer::renderModel для проверки типа..

2 голосов
/ 01 августа 2011

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

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

1 голос
/ 01 августа 2011

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

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

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();
  }
};
1 голос
/ 01 августа 2011

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

0 голосов
/ 01 августа 2011

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

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

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;
};
0 голосов
/ 01 августа 2011

В C ++, разрешение какой перегрузки вызывать, выполняется во время компиляции.

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

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

Приветствия

...