既是基类又可直接使用的类模板

时间:2019-02-26 08:20:51

标签: c++ templates graph-theory

我有两个表示图形的类:

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

如何通过继承使NodeEdge直接可用和可扩展?

3 个答案:

答案 0 :(得分:3)

  

如何使Node和Edge既直接可用又可扩展   虽然继承?

我将以粗体显示该要求。

尽管可以使Edge和Node的定义相互依赖,但无法进行Edge和Node的重复声明,因为这样的声明将产生无限模板递归:

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

因此,如果您希望Edge<>Node<>直接可用(即在不创建虚拟派生类的情况下可实例化),则应该中断该递归。例如,通过使EdgeNode都依赖于某些第三性状类:

// 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;
}

答案 1 :(得分:0)

恕我直言,这可以通过在正确的位置插入前向声明来实现。我使用了OP示例代码的一部分,并将其完成为可编译的示例:

let activityController = UIActivityViewController(activityItems: sharingItems, applicationActivities: nil)
        activityController.excludedActivityTypes = [.print, .mail, .airDrop, .assignToContact, .copyToPasteboard, .addToReadingList, .markupAsPDF, .message, .openInIBooks, .postToTwitter, .postToFacebook]
        present(activityController, animated: true, completion: nil)

Live Demo on coliru

那是实际的把戏:

#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; } template class Node中仔细使用的

OP仅引用和指向相应的指针。相反类型。因此,在两种情况下,不完整的类型都足以用作模板参数。这些不完整的类型由正向声明提供:

template class Edge

然后,可以从// forward declarations: struct WNode; struct WEdge; WNode派生类WEdgeNode<WEdge>

答案 2 :(得分:0)

模板模板参数可能会有所帮助:

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>;

Demo