20 Memory management library [mem]

20.2 Memory [memory]

20.2.8 uses_allocator [allocator.uses]

20.2.8.2 Uses-allocator construction [allocator.uses.construction]

Uses-allocator construction with allocator alloc and constructor arguments args... refers to the construction of an object of type T such that alloc is passed to the constructor of T if T uses an allocator type compatible with alloc.
When applied to the construction of an object of type T, it is equivalent to initializing it with the value of the expression make_obj_using_allocator<T>(alloc, args...), described below.
The following utility functions support three conventions for passing alloc to a constructor:
  • If T does not use an allocator compatible with alloc, then alloc is ignored.
  • Otherwise, if T has a constructor invocable as T(allocator_arg, alloc, args...) (leading-allocator convention), then uses-allocator construction chooses this constructor form.
  • Otherwise, if T has a constructor invocable as T(args..., alloc) (trailing-allocator convention), then uses-allocator construction chooses this constructor form.
The uses_allocator_construction_args function template takes an allocator and argument list and produces (as a tuple) a new argument list matching one of the above conventions.
Additionally, overloads are provided that treat specializations of pair such that uses-allocator construction is applied individually to the first and second data members.
The make_obj_using_allocator and uninitialized_construct_using_allocator function templates apply the modified constructor arguments to construct an object of type T as a return value or in-place, respectively.
[Note 1: 
For uses_allocator_construction_args and make_obj_using_allocator, type T is not deduced and must therefore be specified explicitly by the caller.
— end note]
template<class T, class Alloc, class... Args> constexpr auto uses_allocator_construction_args(const Alloc& alloc, Args&&... args) noexcept;
Constraints: remove_cv_t<T> is not a specialization of pair.
Returns: A tuple value determined as follows:
  • If uses_allocator_v<remove_cv_t<T>, Alloc> is false and is_constructible_v<T, Args...> is true, return forward_as_tuple(std​::​forward<Args>(args)...).
  • Otherwise, if uses_allocator_v<remove_cv_t<T>, Alloc> is true and is_constructible_v<T, allocator_arg_t, const Alloc&, Args...> is true, return tuple<allocator_arg_t, const Alloc&, Args&&...>( allocator_arg, alloc, std::forward<Args>(args)...)
  • Otherwise, if uses_allocator_v<remove_cv_t<T>, Alloc> is true and is_constructible_v<T, Args..., const Alloc&> is true, return forward_as_tuple(std​::​forward<Args>(args)..., alloc).
  • Otherwise, the program is ill-formed.
[Note 2: 
This definition prevents a silent failure to pass the allocator to a constructor of a type for which uses_allocator_v<T, Alloc> is true.
— end note]
template<class T, class Alloc, class Tuple1, class Tuple2> constexpr auto uses_allocator_construction_args(const Alloc& alloc, piecewise_construct_t, Tuple1&& x, Tuple2&& y) noexcept;
Let T1 be T​::​first_type.
Let T2 be T​::​second_type.
Constraints: remove_cv_t<T> is a specialization of pair.
Effects: Equivalent to: return make_tuple( piecewise_construct, apply([&alloc](auto&&... args1) { return uses_allocator_construction_args<T1>( alloc, std::forward<decltype(args1)>(args1)...); }, std::forward<Tuple1>(x)), apply([&alloc](auto&&... args2) { return uses_allocator_construction_args<T2>( alloc, std::forward<decltype(args2)>(args2)...); }, std::forward<Tuple2>(y)));
template<class T, class Alloc> constexpr auto uses_allocator_construction_args(const Alloc& alloc) noexcept;
Constraints: remove_cv_t<T> is a specialization of pair.
Effects: Equivalent to: return uses_allocator_construction_args<T>(alloc, piecewise_construct, tuple<>{}, tuple<>{});
template<class T, class Alloc, class U, class V> constexpr auto uses_allocator_construction_args(const Alloc& alloc, U&& u, V&& v) noexcept;
Constraints: remove_cv_t<T> is a specialization of pair.
Effects: Equivalent to: return uses_allocator_construction_args<T>(alloc, piecewise_construct, forward_as_tuple(std::forward<U>(u)), forward_as_tuple(std::forward<V>(v)));
template<class T, class Alloc, class U, class V> constexpr auto uses_allocator_construction_args(const Alloc& alloc, pair<U, V>& pr) noexcept; template<class T, class Alloc, class U, class V> constexpr auto uses_allocator_construction_args(const Alloc& alloc, const pair<U, V>& pr) noexcept;
Constraints: remove_cv_t<T> is a specialization of pair.
Effects: Equivalent to: return uses_allocator_construction_args<T>(alloc, piecewise_construct, forward_as_tuple(pr.first), forward_as_tuple(pr.second));
template<class T, class Alloc, class U, class V> constexpr auto uses_allocator_construction_args(const Alloc& alloc, pair<U, V>&& pr) noexcept; template<class T, class Alloc, class U, class V> constexpr auto uses_allocator_construction_args(const Alloc& alloc, const pair<U, V>&& pr) noexcept;
Constraints: remove_cv_t<T> is a specialization of pair.
Effects: Equivalent to: return uses_allocator_construction_args<T>(alloc, piecewise_construct, forward_as_tuple(get<0>(std::move(pr))), forward_as_tuple(get<1>(std::move(pr))));
template<class T, class Alloc, pair-like P> constexpr auto uses_allocator_construction_args(const Alloc& alloc, P&& p) noexcept;
Constraints: remove_cv_t<T> is a specialization of pair and remove_cvref_t<P> is not a specialization of ranges​::​subrange.
Effects: Equivalent to: return uses_allocator_construction_args<T>(alloc, piecewise_construct, forward_as_tuple(get<0>(std::forward<P>(p))), forward_as_tuple(get<1>(std::forward<P>(p))));
template<class T, class Alloc, class U> constexpr auto uses_allocator_construction_args(const Alloc& alloc, U&& u) noexcept;
Let FUN be the function template: template<class A, class B> void FUN(const pair<A, B>&);
Constraints: remove_cv_t<T> is a specialization of pair, and either:
  • remove_cvref_t<U> is a specialization of ranges​::​subrange, or
  • U does not satisfy pair-like and the expression FUN(u) is not well-formed when considered as an unevaluated operand.
Let pair-constructor be an exposition-only class defined as follows: class pair-constructor { using pair-type = remove_cv_t<T>; // exposition only constexpr auto do-construct(const pair-type& p) const { // exposition only return make_obj_using_allocator<pair-type>(alloc_, p); } constexpr auto do-construct(pair-type&& p) const { // exposition only return make_obj_using_allocator<pair-type>(alloc_, std::move(p)); } const Alloc& alloc_; // exposition only U& u_; // exposition only public: constexpr operator pair-type() const { return do-construct(std::forward<U>(u_)); } };
Returns: make_tuple(pc), where pc is a pair-constructor object whose alloc_ member is initialized with alloc and whose u_ member is initialized with u.
template<class T, class Alloc, class... Args> constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args);
Effects: Equivalent to: return make_from_tuple<T>(uses_allocator_construction_args<T>( alloc, std::forward<Args>(args)...));
template<class T, class Alloc, class... Args> constexpr T* uninitialized_construct_using_allocator(T* p, const Alloc& alloc, Args&&... args);
Effects: Equivalent to: return apply([&]<class... U>(U&&... xs) { return construct_at(p, std::forward<U>(xs)...); }, uses_allocator_construction_args<T>(alloc, std::forward<Args>(args)...));