У меня есть структурная проблема, с которой я мог бы использовать вашу помощь. Сначала я объясню абстрактную проблему, а затем приведу пример, иллюстрирующий проблему.
Рассмотрим абстрактный класс A , содержащий несколько экземпляров абстрактного класса B , также предположим, что следующие методы в A : void a .foo1 (B val) и B a.foo2 () . Проблема возникает, когда мы наследуем классы ( A ' наследует A и B' наследует B ) и требуем, чтобы отношение A должно B должно совпадать с A ' должно B' . То есть в A ': void a.foo1 (B' val) и B 'a.foo2 () . Второй метод будет работать, но не первый (если мы не делаем небезопасное приведение типов). Другими словами, в A ': a.foo1 (B val) должно быть недопустимым, если параметр не является экземпляром B' . Я попытался смоделировать эти отношения с помощью дженериков / шаблонов с небольшим успехом.
Эта проблема возникает при создании графовой структуры. Здесь у нас есть классы Graph и GraphVertex . (В моей реальной реализации я перегружаю оператор удаления GraphVertex.) В C ++:
template<class T> class Graph; // Forward reference.
template<class T> class GraphVertex
{
public:
GraphVertex(Graph<T> &graph) : m_graph(graph){}
virtual ~GraphVertex() {}
... // Abstract methods, using T parameter.
void remove()
{ m_graph.removeVertex(this); }
protected:
Graph<T> &m_graph;
}
template<class T> class Graph
{
public:
virtual ~Graph(){}
...
virtual GraphVertex<T> *add(T val) = 0;
virtual void removeVertex(GraphVertex<T> *vertex) = 0; // <--- !!!
}
Проблема здесь заключается в методе removeVertex . Допустим, мы реализовали классы с AdjacencyMatrixGraph и AMGVertex . При удалении вершины в AdjacencyMatrixGraph нам нужно больше данных, чем предусмотрено в абстрактном базовом классе GraphVertex . Мы знаем, что тип параметра должен быть AMGVertex , но отправка другого типа в качестве параметра не приведет к ошибке (время компиляции).
Чтобы решить эту проблему, я попытался добавить новый параметр шаблона, указав реализованный тип. То есть:
template<class T, class G> class GraphVertex
{
public:
GraphVertex(G &graph) : m_graph(graph) {}
~GraphVertex() {}
...
void remove() { m_graph.removeVertex(this); }
protected:
G &m_graph;
}
template<class T, class V> class Graph
{
public:
virtual ~Graph() {}
...
virtual V *add(T val) = 0;
virtual void removeVertex(V *vertex) = 0;
}
template<class T> AdjacencyMatrixGraph; // Forward declaration.
template<class T> AMGVertex : public GraphVertex<T, AdjacencyMatrixGraph<T>>
{ ... }
template<class T> AdjacencyMatrixGraph : public Graph<T, AMGVertex<T>>
{ ... }
Однако это не будет работать. Невозможно использовать базовый класс График из-за циклической ссылки на базовые классы.
Graph<int> *p = new AdjacencyMatrixGraph<int>(); // Won't work.
Пример выше имеет те же проблемы в Java с обобщениями.
Итак, есть ли способ смоделировать отношение безопасным для типов образом? Или я застрял с указателями поворота вокруг?
Спасибо за чтение!
EDIT:
Пример использования приведенного выше может выглядеть следующим образом:
Graph<int> *someGraph = getSomeGraph();
GraphVertex<int> *newVertex = someGraph->add(3);
...
newVertex->remove();