Borzh
Borzh

Reputation: 5195

Pointer to template class as that class parameter

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

Answers (2)

Matthieu M.
Matthieu M.

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

Vite Falcon
Vite Falcon

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

Related Questions