Why are are std::allocator's construct and destroy functions deprecated in c++17?
Asked Answered
A

2

63

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?

Awn answered 9/9, 2016 at 15:5 Comment(1)
In fast, they have been removed in C++20.Amann
D
30

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.

Damon answered 9/9, 2016 at 21:32 Comment(3)
I don't understand how are we going to construct an object without using .construct and passing the object's construction arguments to it? "std::allocator's construct and destroy are deprecated because they are useless". Again how else are we going to construct the object? By having the allocator inherit from std::allocator_traits ?Comedy
Allocator-aware containers must use allocator_traits::construct, which defers to the allocator's construct if it exists and supplies a default implementation otherwise. Since std::allocator::construct uses exactly the default implementation, there's no point in providing it.Damon
I've read STL container use std::allocator::construct to value-initialize their elements. What will they use when in C++20? How will that not break code that use containers with custom allocators (which would e.g. overload construct)?Racket
P
16

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.

Precessional answered 9/9, 2016 at 15:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.