Reputation: 5195
Basically while trying to implement a graph through adjacency list, I get stucked defining a graph node:
template <
typename Vertex,
typename Edge,
typename EdgeLink = std::pair< Edge, GraphNode* >,
typename Alloc = std::allocator< EdgeLink >
>
class GraphNode
{
public:
Vertex vertex;
std::list< EdgeLink, Alloc > neighbours;
};
I realize that I can't give parameters to GraphNode template pointer, because they are not defined yet. My question to c++ templates gurus is: what technique is used in this case?
Thanks.
Upvotes: 3
Views: 256
Reputation: 299750
Precising the allocator does not necessitate to precise what the allocator can be used for. For example, in a std::list<T>
the allocator passed is std::allocator<T>
and yet the list
will allocate _ListNode<T>
(implementation defined). This is because allocators need to provide a rebind
mechanism.
template <
typename Vertex,
typename Edge,
typename Allocator = std::allocator<void*>
>
class GraphNode
{
public:
typedef GraphNode<Vertex, Edge, Allocator> NodeType;
typedef std::pair< Edge, NodeType* > LinkType;
typedef typename Allocator::template rebind<LinkType>::other AllocatorType;
Vertex vertex;
std::list< LinkType, AllocatorType > neighbours;
};
In action at ideone.
Note that even though list
will do a rebind
itself, you should still do it, because the allocator types reference
and pointer
(and their const version) will be pulled as typedef
inside the list
.
EDIT: Allow container specification.
This is tricky because the allocator is unfortunately only defined once you are within GraphNode
, you thus need to pass it to the container only within the class, and thus cannot use it in the template outside.
This means using template template parameters, and we thus need to "fix" the arity. Since both vector
and list
only take two parameters we are lucky here, but it might not always hold... fortunately with C++11 allowing template aliases, it won't be too harsh a requirement for the user.
template <
typename Vertex,
typename Edge,
template <typename, typename> class Container = std::vector,
typename Allocator = std::allocator<void*>
>
class GraphNode
{
public:
typedef GraphNode<Vertex, Edge, Container, Allocator> NodeType;
typedef std::pair< Edge, NodeType* > LinkType;
typedef typename Allocator::template rebind<LinkType>::other AllocatorType;
typedef Container<LinkType, AllocatorType> NeighboursType;
Vertex vertex;
NeighboursType neighbours;
};
This can be invoked so:
GraphNode<std::string, int>
GraphNode<std::string, int, std::list>
GraphNode<std::string, int, std::vector>
Demo.
Upvotes: 6
Reputation: 6645
If your EdgeLink is going to be 'inside' of GraphNode, it's better not to declare it as a template parameter. Instead use this approach:
template <
typename Vertex,
typename Edge
>
class GraphNode
{
public:
typedef GraphNode<Vertex, Edge> NodeType;
typedef std::pair< Edge, NodeType* > LinkType;
typedef std::allocator< Linktype > AllocType;
Vertex vertex;
std::list< LinkType, AllocType > neighbours;
};
Upvotes: 1