Class 既是基础 类 又可直接使用的模板
Class templates that are both base classes and directly usable
我有两个 类 表示一个图:
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
既可直接使用又可通过继承进行扩展?
恕我直言,这可以通过在正确的位置插入前向声明来实现。我取了部分 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;
}
这才是真正的诀窍:
OP 在 template class Node
和 template class Edge
中小心使用,仅引用和指向相应的指针。相反的类型。因此,在这两种情况下,不完整的类型都足以用作模板参数。这些不完整的类型由前向声明提供:
// forward declarations:
struct WNode;
struct WEdge;
之后,类WNode
和WEdge
可以从Node<WEdge>
和Edge<WNode>
导出。
How can I make Node and Edge both directly usable and extensible
though inheritance?
我将以粗体显示该要求。
虽然可以让 Edge 和 Node 的 definition 相互依赖,但是没有办法使 Edge 和 Node 的循环 declaration节点,因为这样的声明会产生无限的模板递归:
Node<> = Node<Edge<>> = Node<Edge<Node<>>> = Node<EdgeNode<Edge<>>>> ...
因此,如果您希望 Edge<>
和 Node<>
可直接使用(即可实例化而不使虚拟派生 classes),那么您应该打破该递归。例如,通过使 Edge
和 Node
都依赖于某些第三个特征 class:
// 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;
}
template 模板参数可能有帮助:
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>;
我有两个 类 表示一个图:
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
既可直接使用又可通过继承进行扩展?
恕我直言,这可以通过在正确的位置插入前向声明来实现。我取了部分 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;
}
这才是真正的诀窍:
OP 在 template class Node
和 template class Edge
中小心使用,仅引用和指向相应的指针。相反的类型。因此,在这两种情况下,不完整的类型都足以用作模板参数。这些不完整的类型由前向声明提供:
// forward declarations:
struct WNode;
struct WEdge;
之后,类WNode
和WEdge
可以从Node<WEdge>
和Edge<WNode>
导出。
How can I make Node and Edge both directly usable and extensible though inheritance?
我将以粗体显示该要求。
虽然可以让 Edge 和 Node 的 definition 相互依赖,但是没有办法使 Edge 和 Node 的循环 declaration节点,因为这样的声明会产生无限的模板递归:
Node<> = Node<Edge<>> = Node<Edge<Node<>>> = Node<EdgeNode<Edge<>>>> ...
因此,如果您希望 Edge<>
和 Node<>
可直接使用(即可实例化而不使虚拟派生 classes),那么您应该打破该递归。例如,通过使 Edge
和 Node
都依赖于某些第三个特征 class:
// 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;
}
template 模板参数可能有帮助:
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>;