Issue 3525: uses_allocator_construction_args fails to handle types convertible to pair (original) (raw)
This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++23 status.
3525. uses_allocator_construction_args fails to handle types convertible to pair
Section: 20.2.8.2 [allocator.uses.construction] Status: C++23 Submitter: Tim Song Opened: 2021-02-23 Last modified: 2023-11-22
Priority: 3
View all other issues in [allocator.uses.construction].
View all issues with C++23 status.
Discussion:
As currently specified, the following program is ill-formed (and appears to have been since LWG 2975(i)):
struct S { operator std::pair<const int, int>() const { return {}; } };
void f() { std::pmr::map<int, int> s; s.emplace(S{}); }
There's no matching overload for uses_allocator_construction_args<pair<const int, int>>(alloc, S&&), since S is not a pair and every overload for constructing pairs that takes one non-allocator argument expects a pair from which template arguments can be deduced.
[2021-02-27 Tim adds PR and comments]
The superseded resolution below attempts to solve this issue by adding two additional overloads of uses_allocator_construction_args to handle this case. However, the new overloads forces implicit conversions at the call to uses_allocator_construction_args, which requires the result to be consumed within the same full-expression before any temporary created from the conversion is destroyed. This is not the case for the piecewise_construct overload ofuses_allocator_construction_args, which recursively callsuses_allocator_construction_args for the two elements of the pair, which might themselves be pairs.
The approach taken in the revised PR is to produce an exposition-only_pair-constructor_ object instead. The object holds the allocator and the argument by reference, implicitly converts to the specified specialization of pair, and when so converted return a pair that is constructed by uses-allocator construction with the converted value of the original argument. This maintains the existing design that pair itself doesn't know anything about allocator construction.
Previous resolution [SUPERSEDED]:
This wording is relative to N4878.
- Edit 20.2.2 [memory.syn], header
<memory>synopsis, as indicated:namespace std {
[…]
// 20.2.8.2 [allocator.uses.construction], uses-allocator construction
[…]template<class T, class Alloc>
constexpr auto uses_allocator_construction_args(const Alloc& alloc,
const remove_cv_t& 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 -> see below;template<class T, class Alloc>
constexpr auto uses_allocator_construction_args(const Alloc& alloc,
remove_cv_t&& pr) noexcept;
template<class T, class Alloc, class U, class V>
constexpr auto uses_allocator_construction_args(const Alloc& alloc,
pair<U, V>&& pr) noexcept -> see below;
[…]
} 2. Edit 20.2.8.2 [allocator.uses.construction] as indicated:
template<class T, class Alloc>
constexpr auto uses_allocator_construction_args(const Alloc& alloc,
const remove_cv_t& 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 -> see below;-12- Constraints:
Tis a specialization ofpair. For the second overload,is_same_v<pair<U, V>, remove_cv_t<T>>isfalse.-13- Effects: Equivalent to:
return uses_allocator_construction_args(alloc, piecewise_construct,
forward_as_tuple(pr.first),
forward_as_tuple(pr.second));template<class T, class Alloc>
constexpr auto uses_allocator_construction_args(const Alloc& alloc,
remove_cv_t&& pr) noexcept;
template<class T, class Alloc, class U, class V>
constexpr auto uses_allocator_construction_args(const Alloc& alloc,
pair<U, V>&& pr) noexcept -> see below;-14- Constraints:
Tis a specialization ofpair. For the second overload,is_same_v<pair<U, V>, remove_cv_t<T>>isfalse.-15- Effects: Equivalent to:
return uses_allocator_construction_args(alloc, piecewise_construct,
forward_as_tuple(std::move(pr).first),
forward_as_tuple(std::move(pr).second));
[2021-03-12; Reflector poll]
Set priority to 3 following reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N4878.
- Edit 20.2.2 [memory.syn], header
<memory>synopsis, as indicated:namespace std {
[…]
// 20.2.8.2 [allocator.uses.construction], uses-allocator construction
[…]template<class T, class Alloc, class U, class V>
constexpr auto uses_allocator_construction_args(const Alloc& alloc,
const pair<U, V>& pr) noexcept -> see below;template<class T, class Alloc, class U, class V>
constexpr auto uses_allocator_construction_args(const Alloc& alloc,
pair<U, V>&& pr) noexcept -> see below;template<class T, class Alloc, class U>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, U&& u) noexcept;
[…]
} 2. Add the following to 20.2.8.2 [allocator.uses.construction]:
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:
Tis a specialization ofpair, and the expression_FUN_(u)is not well-formed when considered as an unevaluated operand.-?- Effects: Equivalent to:
return make_tuple(pair-constructor{alloc, u});
where
_pair-constructor_is an exposition-only class defined as follows:struct pair-constructor {
using pair-type = remove_cv_t; // exposition onlyconstexpr operator pair-type() const {
return do-construct(std::forward(u));
}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
};
[2021-12-02 Tim updates PR to avoid public exposition-only members]
[2022-01-31; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
[2022-02-10 Approved at February 2022 virtual plenary. Status changed: Tentatively Ready → WP.]
Proposed resolution:
This wording is relative to N4901.
- Edit 20.2.2 [memory.syn], header
<memory>synopsis, as indicated:namespace std {
[…]
// 20.2.8.2 [allocator.uses.construction], uses-allocator construction
[…]
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;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;template<class T, class Alloc, class U>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, U&& u) noexcept;
[…]
} 2. Add the following to 20.2.8.2 [allocator.uses.construction]:
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:
Tis a specialization ofpair, 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; // exposition onlyconstexpr 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 onlypublic:
constexpr operator pair-type() const {
return do-construct(std::forward(u));
}
};-?- Returns:
make_tuple(_pc_), where_pc_is a_pair-constructor_object whose_alloc_member is initialized withallocand whose_u_member is initialized withu.