Using std :: allocator_traits <a>

I would like to create a class template that accepts a dispenser type (as defined in the standard section 17.6.3.5) as a template argument. I see how std::allocator_traits<A> helps fill in any missing A elements with default settings. Also, is there anything in the standard library or enhance that will help to use the allocator correctly?

In particular:

  • To honor typedefs as std::allocator_traits<A>::propagate_on_container_copy_assignment , do I need to check these things in special member functions of every class that has a member of type A ? Or is there some kind of wrapper that I could use as a member to take care of this?

  • If I want a common data set to reduce the number of distributions by storing additional data next to user-visible objects, is it advisable to multiply the distributor like this?

.

 template<typename T, typename A> class MyClass { private: //... struct storage { int m_special_data; T m_obj; }; typedef typename std::allocator_traits<A>::template rebind_alloc<storage> storage_alloc; typedef typename std::allocator_traits<A>::template rebind_traits<storage> storage_traits; storage_alloc m_alloc; static T* alloc(T&& obj) { storage_traits::pointer sp = storage_traits::allocate(m_alloc, 1); sp->m_special_data = 69105; return ::new(&sp->m_obj) T(std::move(obj)); } //... }; 
+4
source share
1 answer

I don’t know anything to make life easier, allocator_traits really makes it easy to write a dispenser by providing all the boilerplate code, but it doesn’t help using a dispenser.

So that I can use one distributor API in both C ++ 03 and C ++ 11, I added <ext/alloc_traits.h> in GCC 4.7, the class template __gnu_cxx::__alloc_traits provides a consistent API that uses allocator_traits in C ++ 11 and calls the corresponding member functions directly on the distributor in C ++ 03 mode.

  • No, there is no wrapper or shortcut, the requirements for the C ++ 11 dispenser greatly complicate the task of the author of the container. The requirements for each container are slightly different, depending on how it manages the memory. For a vector type in a copy-destination statement, if propagate_on_container_copy_assignment (POCCA) is false and the existing capacity is larger than the size of the original object, you can reuse the existing memory (if POCCA is true and the new allocator is not equal, you cannot reuse the old memory, since it will not be possible to allocate it later after replacing the allocator), but this optimization does not help much for a node-based container, such as a list or map.

  • It looks almost correct, although you probably want to replace

     return ::new(&sp->m_obj) T(std::move(obj)); 

    from

     A a(m_alloc); std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj)); return &sp->m_obj; 

As stated in [container.requirements.general] / 3, containers that use a dispenser use allocator_traits<A>::construct to create the type of the T element itself, but any other allocated types (like your storage ) should not use construct .

If storage itself is built, it will build storage::m_obj , unless this element can be left uninitialized, for example std::aligned_storage<sizeof(T)> , which can be initialized explicitly later with allocator_traits<A>::construct . Alternatively, individually design each element that needs a non-trivial design, for example. if storage also had a string member:

  storage_traits::pointer sp = storage_traits::allocate(m_alloc, 1); sp->m_special_data = 69105; ::new (&sp->m_str) std::string("foobar"); A a(m_alloc); std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj)); return &sp->m_obj; 

The m_special_data element is a trivial type, so its lifetime begins as soon as memory is allocated for it. Members m_str and m_obj need non-trivial initialization, so their lifetime begins when their constructors are completed, which is done by placing a new one and calling construct respectively.

Change I recently found out that the standard has a defect (which I reported), and calls to construct do not need to use a drop dispenser, so these lines:

  A a(m_alloc); std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj)); 

can be replaced by:

  std::allocator_traits<storage_alloc>::construct(m_alloc, &sp->m_obj, std::move(obj)); 

It makes life easier.

+10
source

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


All Articles