3

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.

5
  • 1
    can't you use a forward declaration? class GraphNode; Commented Feb 21, 2012 at 16:06
  • OK, I'm not sure what the question is... Do you want the GraphNode to have a pointer to another GraphNode with unknown template params..? Commented Feb 21, 2012 at 16:13
  • I can't just write GraphNode*, it needs to be GraphNode<Vertex, Edge, EdgeLink, Alloc>* but EdgeLink and Alloc are not defined. Commented Feb 21, 2012 at 16:16
  • Does the user really need to specify EdgeLink (and the Allocator) ? Commented Feb 21, 2012 at 16:18
  • EdgeLink is really not necessary, but I want allocator to be template parameter Commented Feb 21, 2012 at 16:22

2 Answers 2

6

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the info, it is amazing, the things you can do with template programming!
Another quick question: how can I use template parameter to specify container, for example to use std::vector instead of std::list?
@Borzh: updated :) As I said in the answer though, it's trickier than it should because the (proper) allocator only exists within the class.
1

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

5 Comments

The use of identifiers starting with _[A-Z] is forbidden in any scope (reserved to implementation).
Modified my answer. Thanks for that info :)
The problem is that I want allocator to be a template parameter. Could you modify the answer in a way to allocator to be a template parameter?
What information would the 'Edge' type contain? Does it already have information about the two connecting nodes?
Vertex could be a town name and Edge a number that holds distance from one town to another, for example

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.