C++20 removed the construct() and destruct() members from std::allocator. How am I supposed to construct my objects allocated through std::allocator<T>::allocate()? I found std::uninitialized_fill() and std::uninitialized_copy(), but as far as I understood they are not allocator-aware, and they do copies, which I think would hurt performance a lot for non-POD types.
You can do it using std::allocator_traits.
The point of removing the construct method is because the allocator traits already has that method, and the STL containers use std::allocator_traits::construct anyway.
Documentation in cppreference
And here is a little example: (Alloc is any allocator)
Alloc a{};
std::allocator_traits<Alloc>::pointer i = std::allocator_traits<Alloc>::allocate(a, allocated_size);
// You use the construct and destroy methods from the allocator traits
std::allocator_traits<Alloc>::construct(a, i, value_to_construt);
std::allocator_traits<Alloc>::destroy(a, i);
std::allocator_traits<Alloc>::deallocate(a, i, allocated_size);
It looks like a static form of construct still exists in C++20. Maybe that is intended to replace the original form of construct?
https://en.cppreference.com/w/cpp/memory/allocator_traits/construct
I have not really done much with custom allocation so I don't have personal experience using these functions, but it seems reasonable that they might prefer a static function in an effort to break dependencies.
There is also a construct_at function that is added with C++20.
https://en.cppreference.com/w/cpp/memory/construct_at
Related
First I implemented a constexpr-capable pseudo-container using naked new/delete. Then I refactored it to use std::allocator for all its goodness. In doing this, I discovered that there's no way to implement a constexpr uninitialized_default_construct, because you can't use placement new in constexpr, nor is there anything in the C++20 standard with equivalent effects that's also constexpr.
Is there no way around this, without heavy changes to implementations of containers to support default initialization in constexpr? Other than writing a paper to make uninitialized_default_construct constexpr or to introduce something like default_construct_at akin to construct_at?
Use naked new if you want default initialization in constexpr and your container is fixed-size. This means using delete[] rather than std::allocator.deallocate followed by std::destroy. You can still split allocation and construction, but beware to initialize all elements before construction finishes to avoid delete[] from attempting to destroy an object in uninitialized memory.
For non-fixed-size containers, support from std would be necessary, as called out in the question.
The c++17 specification deprecates the construct and destroy members of the std::allocator object. The working group provided rationale for deprecating other member functions here, under the heading "Deprecate the redundant members of std::allocator".
However they don't mention specifically why those two members are deprecated or what the recommendation is for replacing that functionality. I'm assuming the implication is to use std::allocator_traits::construct instead.
I'm a bit confused about whether implementing construct may actually still be necessary in some cases though because of this comment about std::allocator_traits::construct
Because this function provides the automatic fall back to placement new, the member function construct() is an optional Allocator requirement since C++11.
For custom allocators (e.g. for page-aligned memory using memalign), will falling back to placement new always produce the correct behavior?
The allocator requirements table says that construct(c, args), if provided, must "construct an object of type C at c".
It says absolutely nothing about 1) what arguments are to be passed to C's constructor or 2) how these arguments are to be passed. That's the allocator's choice, and in fact two allocators in the standard do mess with the arguments before passing them to C's constructor: std::scoped_allocator_adaptor and std::pmr::polymorphic_allocator. When constructing a std::pair, in particular, the arguments they pass to pair's constructor may not even resemble the ones they received.
There's no requirement to perfectly forward, either; a C++03-style construct(T*, const T&) is conforming if inefficient.
std::allocator's construct and destroy are deprecated because they are useless: no good C++11 and later code should ever call them directly, and they add nothing over the default.
Handling memory alignment should be the task of allocate, not construct.
The functions were removed along with others from the paper D0174R0 Deprecating Vestigial Library Parts in C++17. If we look at the relevant section we have
Many members of std::allocator redundantly duplicate behavior that is otherwise produced by std::allocator_traits<allocator<T>>, and could safely be removed to simplify this class. Further, addressof as a free function supersedes std::allocator<T>::address which requires an allocator object of the right type. Finally, the reference type aliases were initially provided as an expected means for extension with other allocators, but turned out to not serve a useful purpose when we specified the allocator requirements (17.6.3.5 [allocator.requirements]).
While we cannot remove these members without breaking backwards compatibility with code that explicitly used this allocator type, we should not be recommending their continued use. If a type wants to support generic allocators, it should access the allocator's functionality through allocator_traits rather than directly accessing the allocator's members - otherwise it will not properly support allocators that rely on the traits to synthesize the default behaviors. Similarly, if a user does not intend to support generic allocators, then it is much simpler to directly invoke new, delete, and assume the other properties of std::allocator such as pointer-types directly.
Emphasis mine
So the rational was we do not need to duplicate all of the code in allocator since we have the allocator traits. If we look at std::allocator_traits we will see that it does have
allocate
deallocate
construct
destroy
max_size
static functions so we can use those instead of the ones in the allocator.
Here is the documentation on cppreference, here is the working draft.
I must admit that I didn't understand what's the real purpose of polymorphic_allocator and when/why/how I should use it.
As an example, the pmr::vector has the following signature:
namespace pmr {
template <class T>
using vector = std::vector<T, polymorphic_allocator<T>>;
}
What does the polymorphic_allocator offer? What does the std::pmr::vector offer as well in regard of the old-fashioned std::vector?
What can I do now that I wasn't able to do till now?
What's the real purpose of that allocator and when should I use it actually?
Choice quote from cppreference:
This runtime polymorphism allows objects using polymorphic_allocator to behave as if they used different allocator types at run time despite the identical static allocator type
The issue with "regular" allocators is that they change the type of the container. If you want a vector with a specific allocator, you can make use of the Allocator template parameter:
auto my_vector = std::vector<int,my_allocator>();
The problem now is that this vector is not the same type as a vector with a different allocator. You can't pass it to a function which requires a default-allocator vector, for example, or assign two vectors with a different allocator type to the same variable / pointer, eg:
auto my_vector = std::vector<int,my_allocator>();
auto my_vector2 = std::vector<int,other_allocator>();
auto vec = my_vector; // ok
vec = my_vector2; // error
A polymorphic allocator is a single allocator type with a member that can define the allocator behaviour via dynamic dispatch rather than through the template mechanism. This allows you to have containers which use specific, customised allocation, but which are still of a common type.
The customisation of allocator behavior is done by giving the allocator a std::memory_resource *:
// define allocation behaviour via a custom "memory_resource"
class my_memory_resource : public std::pmr::memory_resource { ... };
my_memory_resource mem_res;
auto my_vector = std::pmr::vector<int>(0, &mem_res);
// define a second memory resource
class other_memory_resource : public std::pmr::memory_resource { ... };
other_memory_resource mem_res_other;
auto my_other_vector = std::pmr::vector<int>(0, &mes_res_other);
auto vec = my_vector; // type is std::pmr::vector<int>
vec = my_other_vector; // this is ok -
// my_vector and my_other_vector have same type
The main remaining issue, as I see it, is that a std::pmr:: container is still not compatible with the equivalent std:: container using the default allocator. You need to make some decisions at the time you design an interface which works with a container:
is it likely that the container passed in may require custom allocation?
if so, should I add a template parameter (to allow for arbitrary allocators) or should I mandate the use of a polymorphic allocator?
A template solution allows for any allocator, including a polymorphic allocator, but has other drawbacks (generated code size, compile time, code must be exposed in header file, potential for further "type contamination" which keeps pushing the problem outward). A polymorphic allocator solution on the other hand dictates that a polymorphic allocator must be used. This precludes using std:: containers which use the default allocator, and might have implications for interfacing with legacy code.
Compared to a regular allocator, a polymorphic allocator does have some minor costs, such as the storage overhead of the memory_resource pointer (which is most likely negligible) and the cost of virtual function dispatch for allocations. The main problem, really, is probably lack of compatibility with legacy code which doesn't use polymorphic allocators.
polymorphic_allocator is to a custom allocator as std::function is to a direct function call.
It simply lets you use an allocator with your container without having to decide, at the point of declaration, which one. So if you have a situation where more than one allocator would be appropriate, you can use polymorphic_allocator.
Maybe you want to hide which allocator is used to simplify your interface, or maybe you want to be able to swap it out for different runtime cases.
First you need code that needs an allocator, then you need to want to be able to swap which one is used, before considering pmr vector.
One drawback of polymorphic allocators is that polymorphic_allocator<T>::pointer is always just T*. That means you can't use them with fancy pointers. If you want to do something like place elements of a vector in shared memory and access them through boost::interprocess::offset_ptrs, you need to use a regular old non-polymorphic allocator for that.
So, although polymorphic allocators let you vary allocation behavior without changing a container's static type, they limit what an allocation is.
When elements are default-inserted into an instance of std::vector<T>, they are value-initialized by default. I often work with multi-threaded high-performance codes, where such value-initialization might for large arrays represent an unacceptable sequential bottleneck.
The typical approach based on reserve() and push_back()/emplace_back() is of no use if in concurrent codes. I usually end up with one of the following options:
definition of an empty default constructor for T,
definition and usage of a custom allocator with empty construct() member function.
However, both solutions are far from being elegant and also have drawbacks. The former cannot be used for T being a POD type, such as double. The latter requires a given implementation of the C++ Standard Library to supoort the relatively new DefaultInsertable concept. Moreover, definition of a custom allocator is quite tedious.
Is there any chance that in the future of C++ there will be some straightforward way how to "turn off" this default-insertion/value-initialization?
UPDATE
Mayebe, I should've asked simply if it will be possible to avoid zero-initialization of default-inserted elements of a vector for arithmetic types.
Vector is poorly suited to your needs. It supports resizing and accidental copy, neither of which make sense in a multi-threaded environment.
Write a simple container:
template<class T,class Storage=std::aligned_storage_t<sizeof(T),alignof(T)>{
struct buffer{
static_assert(std::is_pod<T)::value, "pod only");
std::size_t count;
std::unique_ptr<Storage[]> storage;
};
Populate it with container-esque begin/end/front/size/[]/empty etc.
Make it move only.
Use rule of zero (with =default).
Give it a explicit buffer(std::size_t) ctor that creates its content uninitialized.
Mix in some span/array_view types, and this should be suitable for your needs.
Maybe have emplace(size_t,Args&&) which does placement new for you with {}.
I'm currently learning about concurrency in C++ and came across using a vector of threads, which I believe will be possible in C++0x. However, my current compiler doesn't appear to have an implementation of move-aware containers and so I get errors generated because std::thread::thread(const std::thread&) is deleted, ie I can only use the move constructor/move assignment with std::thread.
Am I correct in thinking I could circumvent this issue by writing a custom allocator using
void MyAllocator::construct (pointer p, reference val)
/* should be non-const reference to val because using move constructor? */
{
new ((void*)p) T (std::move(val));
}
rather than
void allocator::construct (pointer p, const_reference val)
{
new ((void*)p) T (val);
}
? Or some other variation on this theme (possibly using an overload of MyAllocator::construct).
NB: This is mainly intended to be a short-term educational exercise and well enough performing work around to play around with threads in containers. I'd only be using MyAllocator in this context. However, please also point me at any libraries that may have this implemented so I can have a poke around the source.
If your compiler doesn't provide a move-aware std::vector then you'll have to write your own specialization of std::vector<std::thread> rather than just provide a custom allocator. The whole C++03 vector interface relies on copying: push_back() copies elements in; resize() initializes the empty elements with a copy of the element passed as the second parameter (even if that is the default value of T()); resize(), reserve(), insert(), erase() and push_back() will copy elements if the vector needs reallocating, or elements otherwise need moving around, and so forth.
This is such a common problem that I've included such a specialization with my (commercial) just::thread implementation of std::thread.
The easiest way to circumvent the problem would be to allocate the threads on the heap and manipulate pointers to them.
Check the Boost Pointer Container library: boost::ptr_vector<std::thread> seems to me what you are looking for.
The requirement that std containers only take copyable objects has more to do with the C++03 container interfaces than it does with the allocator implementation.
For example
vector<T> b(100);
vector<T> a;
a=b;
assert(a==b);
The standard assures us a==b is true. However, if T were not copyable, then in the best case a=b will not compile, in the worst a=b is undefined. Furthermore,
a.push_back(T());
may cause a to allocate new space, and under the hood there are copies made to the new underlying storage from the old.
Furthermore, there is nothing in the C++03 standard that says an implementation actually has to call allocator.construct, and in fact many (gcc for example) do not.
The C++0x standard adds new member functions to the container interface for moveable types, and clarifies how things like operator= behave in their presence.
See www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2486.pdf