C++17 adds std::destroy_at
, but there isn't any std::construct_at
counterpart. Why is that? Couldn't it be implemented as simply as the following?
template <typename T, typename... Args> T* construct_at(void* addr, Args&&... args) { return new (addr) T(std::forward<Args>(args)...); }
Which would enable to avoid that not-entirely-natural placement new syntax:
auto ptr = construct_at<int>(buf, 1); // instead of 'auto ptr = new (buf) int(1);' std::cout << *ptr; std::destroy_at(ptr);
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 .
std::any. The class any describes a type-safe container for single values of any copy constructible type. 1) An object of class any stores an instance of any type that satisfies the constructor requirements or is empty, and this is referred to as the state of the class any object.
std::destroy_at
provides two objective improvements over a direct destructor call:
It reduces redundancy:
T *ptr = new T; //Insert 1000 lines of code here. ptr->~T(); //What type was that again?
Sure, we'd all prefer to just wrap it in a unique_ptr
and be done with it, but if that can't happen for some reason, putting T
there is an element of redundancy. If we change the type to U
, we now have to change the destructor call or things break. Using std::destroy_at(ptr)
removes the need to change the same thing in two places.
DRY is good.
It makes this easy:
auto ptr = allocates_an_object(...); //Insert code here ptr->~???; //What type is that again?
If we deduced the type of the pointer, then deleting it becomes kind of hard. You can't do ptr->~decltype(ptr)()
; since the C++ parser doesn't work that way. Not only that, decltype
deduces the type as a pointer, so you'd need to remove a pointer indirection from the deduced type. Leading you to:
auto ptr = allocates_an_object(...); //Insert code here using delete_type = std::remove_pointer_t<decltype(ptr)>; ptr->~delete_type();
And who wants to type that?
By contrast, your hypothetical std::construct_at
provides no objective improvements over placement new
. You have to state the type you're creating in both cases. The parameters to the constructor have to be provided in both cases. The pointer to the memory has to be provided in both cases.
So there is no need being solved by your hypothetical std::construct_at
.
And it is objectively less capable than placement new. You can do this:
auto ptr1 = new(mem1) T; auto ptr2 = new(mem2) T{};
These are different. In the first case, the object is default-initialized, which may leave it uninitialized. In the second case, the object is value-initialized.
Your hypothetical std::construct_at
cannot allow you to pick which one you want. It can have code that performs default initialization if you provide no parameters, but it would then be unable to provide a version for value initialization. And it could value initialize with no parameters, but then you couldn't default initialize the object.
Note that C++20 added std::construct_at
. But it did so for reasons other than consistency. They're there to support compile-time memory allocation and construction.
You can call the "replaceable" global new
operators in a constant expression (so long as you haven't actually replaced it). But placement-new isn't a "replaceable" function, so you can't call it there.
Earlier versions of the proposal for constexpr allocation relied on std::allocator_traits<std::allocator<T>>::construct/destruct
. They later moved to std::construct_at
as the constexpr
construction function, which construct
would refer to.
So construct_at
was added when objective improvements over placement-new could be provided.
There is such a thing, but not named like you might expect:
uninitialized_copy copies a range of objects to an uninitialized area of memory
uninitialized_copy_n (C++11) copies a number of objects to an uninitialized area of memory (function template)
uninitialized_fill copies an object to an uninitialized area of memory, defined by a range (function template)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With