Шаблоны классов, которые являются как базовыми, так и непосредственно используемыми - PullRequest
0 голосов
/ 26 февраля 2019

У меня есть два класса, представляющих граф:

class Node {
public:
   void AppendSource(Edge &Edge) { m_Sources.append(&Edge); }
   void AppendSink(Edge &Edge) { m_Sinks.append(&Edge); }
   QList<Edge *> &Sources() { return m_Sources; }
   QList<Edge *> &Sinks() { return m_Sinks; }
   QList<Edge *> const &Sources() const { return m_Sources; }
   QList<Edge *> const &Sinks() const { return m_Sinks; }
protected:
   QList<Edge *> m_Sources;
   QList<Edge *> m_Sinks;
}; // Node

class Edge {
public:
   Edge(Node &Source, Node &Sink) : m_pSource(&Source), m_pSink(&Sink) {}
   Node const &Source() const { return *m_pSource; }
   Node const &Sink() const { return *m_pSink; }
   Node &Source() { return *m_pSource; }
   Node &Sink() { return *m_pSink; }
   void SetSource(Node &Source) { m_pSource = &Source; }
   void SetSink(Node &Sink) { m_pSink = &Sink; }
protected:
   Node *m_pSource;
   Node *m_pSink;
}; // Edge

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

template <class EDGE_TYPE>
class Node {
public:
   void AppendSource(EDGE_TYPE &Edge) { m_Sources.append(&Edge); }
   void AppendSink(EDGE_TYPE &Edge) { m_Sinks.append(&Edge); }
   QList<EDGE_TYPE *> &Sources() { return m_Sources; }
   QList<EDGE_TYPE *> &Sinks() { return m_Sinks; }
   QList<EDGE_TYPE *> const &Sources() const { return m_Sources; }
   QList<EDGE_TYPE *> const &Sinks() const { return m_Sinks; }
protected:
   QList<EDGE_TYPE *> m_Sources;
   QList<EDGE_TYPE *> m_Sinks;
}; // Node


template <class NODE_TYPE>
class Edge {
public:
   Edge(NODE_TYPE &Source, NODE_TYPE &Sink) : m_pSource(&Source), m_pSink(&Sink) {}
   NODE_TYPE const &Source() const { return *m_pSource; }
   NODE_TYPE const &Sink() const { return *m_pSink; }
   NODE_TYPE &Source() { return *m_pSource; }
   NODE_TYPE &Sink() { return *m_pSink; }
   void SetSource(NODE_TYPE &Source) { m_pSource = &Source; }
   void SetSink(NODE_TYPE &Sink) { m_pSink = &Sink; }
protected:
   NODE_TYPE *m_pSource;
   NODE_TYPE *m_pSink;
}; // Edge

Но теперь, кажется, больше невозможно использовать классы без их расширения!Несколько попыток с очевидными соответствующими ошибками:

new Node(); // 'Node': use of class template requires template argument list
new Node<>(); // 'Node': too few template arguments
new Node<Edge>(); // 'Edge': unspecialized class template can't be used as a template argument for template parameter 'EDGE_TYPE', expected a real type
new Node<Edge<>>(); // 'Edge': too few template arguments
new Node<Edge<Node>>(); // 'Node': unspecialized class template can't be used as a template argument for template parameter 'NODE_TYPE', expected a real type
new Node<Edge<Node<>>>(); // 'Node': too few template arguments

Я надеялся решить эту проблему, введя значения по умолчанию для аргументов шаблона.Несколько попыток с соответствующими ошибками:

template <class EDGE_TYPE = Edge>
class Node { ... }

template <class NODE_TYPE = Node>
class Edge { ... }

new Node<>(); // 'Edge': unspecialized class template can't be used as a template argument for template parameter 'EDGE_TYPE', expected a real type
template <class EDGE_TYPE = Edge<>>
class Node { ... }

template <class NODE_TYPE = Node<>>
class Edge { ... }

new Node<>(); // recursive type or function dependency context too complex
template <class EDGE_TYPE = Edge<Node<EDGE_TYPE>>>
class Node { ... }


template <class NODE_TYPE = Node<Edge<NODE_TYPE>>>
class Edge { ... }

new Node<>(); // 'EDGE_TYPE': undeclared identifier

Как я могу сделать Node и Edge как непосредственно используемыми, так и расширяемыми через наследование?

Ответы [ 3 ]

0 голосов
/ 26 февраля 2019

Как сделать так, чтобы Node и Edge могли использоваться напрямую и могли быть расширены через наследование?

Я выделю это требование жирным шрифтом.

Хотя у вас может быть определение Edge и Node, зависящее друг от друга, невозможно сделать повторяющееся объявление Edge и Node, потому что такое объявление даст бесконечную рекурсию шаблона:

Node<> = Node<Edge<>> = Node<Edge<Node<>>> = Node<EdgeNode<Edge<>>>> ...

Итак, если вы хотите, чтобы Edge<> и Node<> были непосредственно использованы (т. Е. Создавались без создания фиктивных производных классов), то вы должны прервать эту рекурсию.Например, зависимость Edge и Node зависит от некоторого третьего класса черт:

// Forward declaration.
struct DefaultGraphTypes;

template <typename GraphTypes = DefaultGraphTypes>
struct Node;

template <typename GraphTypes = DefaultGraphTypes>
struct Edge;


// Traits class.
template <typename NodeT, typename EdgeT>
struct GraphTypes
{
    // Could change this to 'using' in modern C++
    typedef NodeT   FinalNodeType;
    typedef EdgeT   FinalEdgeType;

    // typedef MayBeSomeOtherParameters ...
};

struct DefaultGraphTypes
 : public GraphTypes<Node<DefaultGraphTypes>, Edge<DefaultGraphTypes>>
{
};


// Implementation of graph classes.
template <typename GraphTypes>
struct Node
{
    typedef typename GraphTypes::FinalNodeType    FinalNodeType;
    typedef typename GraphTypes::FinalEdgeType    FinalEdgeType;

    // ... Your implementation
};

template <typename GraphTypes>
struct Edge
{
    typedef typename GraphTypes::FinalNodeType    FinalNodeType;
    typedef typename GraphTypes::FinalEdgeType    FinalEdgeType;

    // ... Your implementation
};


//  User-derived types.
struct MyNode;
struct MyEdge;

struct MyNode
 : public Node<GraphTypes<MyNode, MyEdge>>
{
    // Something specific
};

struct MyEdge
 : public Edge<GraphTypes<MyNode, MyEdge>>
{
    // Something specific
};


// Test
int main()
{
    Node<>       n1;
    Edge<>       e1;

    MyNode       n2;
    MyEdge       e2;

    return 0;
}
0 голосов
/ 26 февраля 2019

Параметр шаблона шаблона может помочь:

template <typename TEdge>
class Node {
public:
   using EDGE_TYPE = TEdge;

   void AppendSource(EDGE_TYPE &Edge) { m_Sources.append(&Edge); }
   void AppendSink(EDGE_TYPE &Edge) { m_Sinks.append(&Edge); }
   QList<EDGE_TYPE *> &Sources() { return m_Sources; }
   QList<EDGE_TYPE *> &Sinks() { return m_Sinks; }
   QList<EDGE_TYPE *> const &Sources() const { return m_Sources; }
   QList<EDGE_TYPE *> const &Sinks() const { return m_Sinks; }
protected:
   QList<EDGE_TYPE *> m_Sources;
   QList<EDGE_TYPE *> m_Sinks;
}; // Node

template <template <typename> class TNode>
class Edge {
public:
    using NODE_TYPE = TNode<Edge>; // which is TNode<Edge<TNode>>

    Edge(NODE_TYPE &Source, NODE_TYPE &Sink) : m_pSource(&Source), m_pSink(&Sink) {}
    NODE_TYPE const &Source() const { return *m_pSource; }
    NODE_TYPE const &Sink() const { return *m_pSink; }
    NODE_TYPE &Source() { return *m_pSource; }
    NODE_TYPE &Sink() { return *m_pSink; }
    void SetSource(NODE_TYPE &Source) { m_pSource = &Source; }
    void SetSink(NODE_TYPE &Sink) { m_pSink = &Sink; }
protected:
    NODE_TYPE *m_pSource;
    NODE_TYPE *m_pSink;
};

Тогда у вас может быть:

using MyEdge = Edge<Node>;
using MyNode = Node<Edge<Node>>; // Node<MyEdge>

или даже:

template <template <typename> class TNode>
class CustomEdge : Edge<TNode> {
    // ...
};

using MyNode2 = Node<CustomEdge>;
using MyEdge2 = CustomEdge<Node>;

Демо

0 голосов
/ 26 февраля 2019

ИМХО, этого можно добиться, вставив предварительные декларации в нужное место.Я взял части кода образца OP и заполнил его для скомпилированного образца:

#include <iostream>
#include <vector>

#define QList std::vector // Sorry, no Qt at hand in coliru

template <class EDGE_TYPE>
class Node {
public:
   void AppendSource(EDGE_TYPE &Edge) { m_Sources.append(&Edge); }
   void AppendSink(EDGE_TYPE &Edge) { m_Sinks.append(&Edge); }
   QList<EDGE_TYPE *> &Sources() { return m_Sources; }
   QList<EDGE_TYPE *> &Sinks() { return m_Sinks; }
   QList<EDGE_TYPE *> const &Sources() const { return m_Sources; }
   QList<EDGE_TYPE *> const &Sinks() const { return m_Sinks; }
protected:
   QList<EDGE_TYPE *> m_Sources;
   QList<EDGE_TYPE *> m_Sinks;
}; // Node


template <class NODE_TYPE>
class Edge {
public:
   Edge(NODE_TYPE &Source, NODE_TYPE &Sink) : m_pSource(&Source), m_pSink(&Sink) {}
   NODE_TYPE const &Source() const { return *m_pSource; }
   NODE_TYPE const &Sink() const { return *m_pSink; }
   NODE_TYPE &Source() { return *m_pSource; }
   NODE_TYPE &Sink() { return *m_pSink; }
   void SetSource(NODE_TYPE &Source) { m_pSource = &Source; }
   void SetSink(NODE_TYPE &Sink) { m_pSink = &Sink; }
protected:
   NODE_TYPE *m_pSource;
   NODE_TYPE *m_pSink;
}; // Edge

// forward declarations:
struct WNode;
struct WEdge;

// declaration of derived types

struct WNode: public Node<WEdge>
{
  int weight;
};

struct WEdge: public Edge<WNode>
{
  int weight;
  WEdge(WNode &src, WNode &snk): Edge(src, snk) { }
};

// check whether it compiles

int main()
{
  WNode node1, node2;
  WEdge edge12(node1, node2);
  // done
  return 0;
}

Live Demo на coliru

Это фактический трюк:

ОП тщательно используется в template class Node и template class Edge только ссылки и указатели на соотв.противоположный тип.Следовательно, неполного типа вполне достаточно для использования в качестве аргумента шаблона в обоих случаях.Эти неполные типы предоставляются в предварительных декларациях:

// forward declarations:
struct WNode;
struct WEdge;

Впоследствии классы WNode и WEdge могут быть получены из Node<WEdge> и Edge<WNode>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...