Я наткнулся на проблему, которую я (и никто из моих более знающих коллег) не знаю, как ее решить (обойти). В конце концов, проблема заключается в том, что невозможно создать функцию виртуального шаблона. Я тщательно обыскал сеть, нашел несколько способов борьбы с ней, но, похоже, ни один из них не применим в моем случае.
Я не знаю, как описать эту ситуацию вкратце, но я приложу все усилия и надеюсь, что это имеет смысл.
Проблема заключается в обработке двух кривых, каждая из которых состоит из одного или нескольких сегментов одинаковых или разных типов кривых. Поэтому я начал с создания интерфейса для сегмента кривой с классом шаблона кривой, с которым он будет взаимодействовать (например, функции того же типа, что и пересечения между двумя различными сегментами кривой):
template<class curve> class curve_segment { // some methods here }
пользователь может затем реализовать несколько типов кривых и соответствующие функции в зависимости от того, с какими кривыми он будет взаимодействовать. Например, circle
а также line
где оба могут взаимодействовать с обоими:
class line : public curve_segment<line>, public curve_segment<circle> { //... }
class circle : public curve_segment<line>, public curve_segment<circle> { //... }
После этого у меня есть класс cell
, который зависит от двух сегментов кривой и базового класса cell_base
который инкапсулирует это:
template<class curve1, class curve2> class cell : public cell_base {
cell_base* up; cell_base* down;
curve_segment<curve1>* segment_x;
curve_segment<curve2>* segment_y;
// some methods that depend on both curves
}
В конце концов, существует двумерная сетка m * n таких ячеек, где две кривые состоят из m и n curve_segments
возможно разных типов, и удерживается вместе двумя указателями в каждой ячейке.
Проблема начинает показывать, когда новый сегмент кривой будет добавлен к одной из двух кривых. Очевидное решение было бы добавить
template<class curve> virtual void add_curve_x(curve_segment<curve> seg) =0;
к cell_base
класс, где реализация в cell
может извлечь соответствующий сегмент другой кривой и добавить к нему новую ячейку. Например, если кривые a
а также b
где a
представляет ось х в сетке, и мы должны были добавить еще один сегмент кривой к кривой a
мы могли бы найти правую руку в большинстве ячеек сетки, извлечь кривую на оси у каждой такой ячейки, и из этого и вновь предоставленного сегмента создать новую ячейку, которая добавится к ее правому краю.
Стирание типа не сработает, так как нам нужно знать тип вновь добавленного сегмента в точке cell
создание (нам нужно предоставить типы обоих сегментов в качестве параметров шаблона для cell
учебный класс).
Перемещение шаблона в cell_base
класс также не будет работать, так как это будет означать, что во время каждого cell
распределение я должен был бы знать тип следующего curve_segment
,
Это можно обойти?
редактировать:
Как и предполагалось, добавление add_curve_x
метод:
template<class curve1, class curve2>
template<class curve>
cell<curve1, curve>* cell<curve1, curve2>::add_curve_x(curve_segment<curve1>* seg) {
return new cell<curve1, curve>(seg, (curve_segment<curve>*)(this->segment_y));
}
В этом случае seg
должен быть типа curve
, который должен реализовать curve_segment<curve1>
, segment_y
также должен реализовать curve_segment<curve>
,
редактировать 2: объяснение, почему актеры там
Возьмите случай с не полужирным прямоугольником. В этих ситуациях есть две кривые, кривая a
с 2 сегментами и кривой b
с 3 сегментами. учебный класс line
должен реализовать curve_segment<line>
так же как curve_segment<circle>
, и круг должен реализовать и то и другое. Теперь давайте добавим еще один сегмент кривой Безье к кривой a
, учебный класс bezier
должен реализовать curve_segment<line>
а также curve_segment<circle>
, а также line
а также circle
должны реализовать curve_segment<bezier>
,
Давайте посмотрим, как мы создаем середину жирной тройки cells
, Мы бы позвонили
add_curve_x<bezier>(new bezier(...));
на объекте типа ячейки cell<circle, circle>
,
Аргумент seg
будет новым bezier
объект (который может быть приведен к curve_segment<circle>
), и это также то, что curve
тип есть, и segment_y
должен быть приведен к curve_segment<bezier>
, поскольку это тип кривой, с которой он взаимодействует во вновь созданной ячейке.
добавлять curve_segment_base
:
class curve_segment_base {
// for double dispatching here:
virtual cell_base* add_curve_x(cell_base* cell) = 0;
};
template<class curve>
class curve_segment : public virtual curve_segment_base {
// some specific methods here
// for double dispatching here:
virtual cell_base* add_curve_x(cell_base* cell)
{
// this has to be moved to cpp file due to dependency from cell_base
return cell->add_curve_x(this); // now the correct method from cell is called
}
};
Таким образом, вы можете добавить этот метод как виртуальный к своей cell_base
class cell_base {
virtual cell_base* add_curve_x(curve_segment_base* seg) = 0;
// methods necessary for double dispatching here:
virtual cell_base* add_curve_x(curve_segment<line>* seg) = 0;
virtual cell_base* add_curve_x(curve_segment<circle>* seg) = 0;
...
};
Итак, ваш метод будет выглядеть так, с двойной диспетчеризацией здесь:
template<class curve1, class curve2>
virtual cell_base* cell<curve1, curve2>::add_curve_x(curve_segment_base* seg) {
return seg->add_curve_x(this);
}
И вам придется реализовать все реальные методы реализации для всех сегментов:
template<class curve1, class curve2>
virtual cell_base* cell<curve1, curve2>::add_curve_x(curve_segment<line>* seg) {
return new cell<curve1, line>(...);
}
template<class curve1, class curve2>
virtual cell_base* cell<curve1, curve2>::add_curve_x(curve_segment<circle>* seg) {
return new cell<curve1, circle>(...);
}
Других решений пока нет …