Another curious repeating pattern

template <class Data, class Allocator = std::allocator<Node> >
class Node : public Data {
  // ...
};

      

The question is simple, how to make the code above? The goal is to give Node the ability to assign other nodes (and provide a default allocator).

+2


source to share


5 answers


You cannot write it like this:

template <class Data, class Allocator>
class Node;

template <class Data, class Allocator = 
  std::allocator<Node<Data, std::allocator<Node<...> >
class Node : public Data {
  // ...
};

      

Because the default argument is to repeat itself. You can use tag-type though



struct DefaultAllocatorTag { };

template<typename Alloc, typename Node>
struct SelectAllocator {
  typedef Alloc type;
};

template<typename Node>
struct SelectAllocator<DefaultAllocatorTag, Node> {
  typedef std::allocator<Node> type;
};

template <class Data, class Allocator = DefaultAllocatorTag >
class Node : public Data {
  typedef typename SelectAllocator<Allocator, Node>::type 
    NodeAllocator;
};

      

If applicable, I would define an allocator in a container. Like this:

template<typename Data, typename Allocator = std::allocator<Data> >
struct Container {
  struct Node : Data { 
    typedef typename Allocator::template rebind<Node>::other NodeAllocator;
    ...
  };
  ...
};

      

+5


source


Finally I solved it! The solution is to defer the specialization of the default allocator until inside a class where Node is already defined:
template <class Data, template<class T> class TAllocator = std::allocator >
class Node : public Data {
  typedef TAllocator<Node> Allocator;
  // ...
};

      



+5


source


How about this ?:

#include <memory>

template<class Data>
class NodeImpl : public Data
{
};

template<class Data, class Allocator = std::allocator< NodeImpl<Data> > >
class Node : public NodeImpl<Data>
{
};

class MyAllocator
{
};

class MyDataClass
{
};

int main()
{
    Node<MyDataClass> node;

    Node<MyDataClass, MyAllocator> node_with_alloc;

    return 0;
}

      

+1


source


You cannot compile it - what you are trying to create is an "infinite" type.

To begin with, you cannot use an uninstalled template template as a template argument. So you need to pass Node to std :: allocator like:

template <class Data, class Allocator = std::allocator<Node<Data, Something> > > 
class Node ...

      

However, what would it be? Well, std :: allocator

The trick is that allocators have to allocate not only the template argument, but any other type as well. Declare the class as

template <class Data, class Allocator = std::allocator<Data> > class Node ...

      

Then create an allocator for nodes like this:

typename Allocator::rebind<Node>::other nodeAllocator(myDataAllocator)

      

This vcblog post about allocators could help, although it focused too much on iterators.

+1


source


Another solution. This seems to be more typical. I.e. the implementation of vectors and smart pointers uses something similar. The idea is to privately inherit from an allocator:

template <class Data, template <class N> class Allocator = std::allocator>
class Node : public Data, private Allocator<Node<Data, Allocator> > {
  // ...
};

      

The bonus is that we can already use Node.

0


source







All Articles