Nested class as template parameter

I am trying to write a special container in STL style. For the sake of simplicity, let's say a list. I looked at the standard way to define such a container:

template <typename T, typename A = std::allocator<T> > class mylist; 

Now I want to manage list nodes using a nested class:

 (inside mylist) class node { T data; node *next; } 

As far as I understand, I do not need to specify the template specifier before the node definition, since the compiler will create separate classes mylist<T,A>::node for each combination of template parameters mylist .

However, now I need to allocate memory not only for data of type T , but also for their node wrapper. Thus, I would like the default template parameter to be of type std::allocator<mylist<T>::node> . At this point, however, mylist has not yet been declared, and the compiler is clearly upset:

 error: `mylist' was not declared in this scope 

How to solve this riddle? There are two limitations:

  • I usually declare a missing class without fully declaring its contents. However, since it is nested inside the very thing that I want to declare, this is not an option.
  • I need the node be nested since it needs to access the instance of the mylist distributor. For example, I have operator= declared on node , where a lot of memory management happens recursively. This may be redundant for the list, and you can do it from mylist , thereby reducing the parametric dependence of node on A , but it is crucial for the data structure that I implement.
+5
source share
2 answers

It does not matter what the default dispenser type argument is, only the actual type. You can use rebind_alloc from std::allocator_traits :

Alloc::rebind<T>::other if present, otherwise Alloc<T, Args> if this Alloc is Alloc<U, Args>

to get what you need:

 template <typename T, typename A = std::allocator<T> > class mylist { class node { ... }; using NodeAlloc = typename std::allocator_traits<A>::template rebind_alloc<node>; }; 

And then use NodeAlloc to get node s. That way, if the user does not specify a distributor, you will get the default value std::allocator<T> , and then use std::allocator<node> . This is exactly what you want, without having to expose node .

+2
source

I need the node to be nested since it needs to access the mylist distributor mylist

Not sure. They can be friends:

 template <typename, class> class list; template <typename T> struct node { // ... }; template <typename T, class Alloc=std::allocator<T> > class list { friend node<T>; // ... }; 

If you do not want the node be accessible outside your file, just drop it into the header file ( .h / .hpp ).

0
source

Source: https://habr.com/ru/post/1235810/


All Articles