Do custom allocators in the STL only perform actual data?

Let's say I create a linked list in the STL:

list<int, my_allocator<int> > data; 

Then I can use a more efficient allocator, say, a memory pool. But shouldn't a list allocate internal memory, such as forward and backward pointers, to traverse the list? How will they be distributed? Using regular new or somehow using my_allocator ?

+7
source share
1 answer

The container really reassigns your dispenser to distribute your own accounting material. (Not that it would matter for std::list , but in reality it is true. *) This is why the requirements of a standard distributor require the existence of a rebind pattern:

 typedef typename Alloc::template rebind<MyInternalStuff>::other internal_allocator; 

If your allocator is Alloc = my_allocator<T> , then internal_allocator becomes my_allocator<MyInternalStuff> .

I believe this was one of the problems Electronic Arts had with the C ++ standard library, so their EASTL library uses different convention for distributors, which offers tighter control.

*) Usually each node will be one monolithic object of some type Node<T> , so I assume that std::list<T, Alloc> uses Alloc::rebind<Node<T>>::other only as a distributor.

[Sorry for a few changes; I had a failure, and he did not interpret it correctly; Now I went and printed each container separately and accordingly corrected the output. std::list really only needs one allocator.]


Update: Just for a giggle, I wrote a small block distributor that prints its own name when building. Here's the input:

 #include <unordered_map> #include <set> #include <deque> #include <list> #include <vector> #include <map> #include <iostream> int main() { std::cout << "----- unordered_map<int, double> -----------" << std::endl; std::unordered_map<int, double, std::hash<int>, std::equal_to<int>, funky_allocator<std::pair<const int, double>>> m { {1, 1.2} }; std::cout << "----- set<int> -----------------------------" << std::endl; std::set<int, std::less<int>, funky_allocator<int>> s; std::cout << "----- deque<int> ---------------------------" << std::endl; std::deque<int, funky_allocator<int>> d; std::cout << "----- list<int> ----------------------------" << std::endl; std::list<int, funky_allocator<int>> l; std::cout << "----- vector<int> --------------------------" << std::endl; std::vector<int, funky_allocator<int>> c; std::cout << "----- map<int, bool> -----------------------" << std::endl; std::map<int, bool, std::less<int>, funky_allocator<std::pair<const int, bool>>> n { { 1, true } }; } 

And here is the conclusion:

 ----- unordered_map<int, double> ----------- Default-construct: funky_allocator<std::pair<int const, double> > Copy-construct: funky_allocator<std::__detail::_Hash_node<std::pair<int const, double>, false> > Copy-construct: funky_allocator<std::__detail::_Hash_node<std::pair<int const, double>, false>*> ----- set<int> ----------------------------- Default-construct: funky_allocator<std::_Rb_tree_node<int> > ----- deque<int> --------------------------- Default-construct: funky_allocator<int> Copy-construct: funky_allocator<int*> ----- list<int> ---------------------------- Default-construct: funky_allocator<std::_List_node<int> > ----- vector<int> -------------------------- Default-construct: funky_allocator<int> ----- map<int, bool> ----------------------- Default-construct: funky_allocator<std::_Rb_tree_node<std::pair<int const, bool> > > 

Details depend on which constructor is used: Containers of type set and map can only create the β€œcorrect” dispenser in some call, and in another they can first create an object of the specified dispenser. In any case, the specified dispenser will never be used at all for several containers, and only the version of the rebound is used.

+10
source

All Articles