This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
allocate_shared is inconsistent about removing const from the pointer
passed to allocator construct and destroySection: 20.3.2.2.7 [util.smartptr.shared.create] Status: New Submitter: Billy O'Neal III Opened: 2019-05-29 Last modified: 2024-10-02
Priority: 3
View all other issues in [util.smartptr.shared.create].
View all issues with New status.
Discussion:
I implemented the fix for LWG 3008(i) and Stephan pointed out there's an inconsistency here
for allocate_shared<const T>.
destroy
call is done with removed cv qualifiers.
The fallback for allocator_traits::construct rejects const T* (since it static_casts
to void*), so the most likely outcome of attempting to do this today is to fail to compile, which
is a break with C++17.
Our options are:
Fix the allocator model to deal with const elements somehow, which breaks compatibility
with existing allocators unprepared for const elements here. We would need to extend the allocator
requirements to allow const T* to be passed here, and fix our default to const_cast.
Fix allocate_shared to remove const before calling construct, which
changes the experience for C++17 customers because allocate_shared constructs a T
instead of a const T, but not in a way substantially different to edits
P0674 already made here.
Back out allocate_shared's interaction with this part of the allocator model (reverting
this part of P0674 and reopening LWG 3008(i)).
Go around the problem by prohibiting allocate_shared<const T>, which breaks
existing C++17 customers.
Billy O'Neal argues that only (2) preserves the design intent P0674 while maintaining compatibility for most allocators and most C++17 customers.
Peter Dimov argues that (1) isn't likely to break enough to matter.[2019-06-16 Priority set to 3 based on reflector discussion]
Previous resolution [SUPERSEDED]:
This wording is relative to N4810.
[Drafting note: As the issue submitter prefers option (2), this is wording for that.]
Modify 20.3.2.2.7 [util.smartptr.shared.create] as indicated:
template<class T, ...> shared_ptr<T> make_shared(args); template<class T, class A, ...> shared_ptr<T> allocate_shared(const A& a, args); template<class T, ...> shared_ptr<T> make_shared_default_init(args); template<class T, class A, ...> shared_ptr<T> allocate_shared_default_init(const A& a, args);-2- Requires: […]
[…] -7- Remarks:
(7.1) — […]
[…]
(7.5) — When a (sub)object of a non-array type
Uis specified to have an initial value ofv, orU(l...), wherel...is a list of constructor arguments,allocate_sharedshall initialize this (sub)object via the expression
(7.5.1) —
allocator_traits<A2>::construct(a2, pv, v)or(7.5.2) —
allocator_traits<A2>::construct(a2, pv, l...)respectively, where
pvpoints to storage suitable to hold an object of typeremove_cv_t<U>anda2of typeA2is a rebound copy of the allocatorapassed toallocate_sharedsuch that itsvalue_typeisremove_cv_t<U>.
[2024-04-13; Jiang An comments and provides improved wording]
The currently proposed resolution is meaningless, because "(allocated) storage suitable to hold an object of type
remove_cv_t<U>" is always "storage suitable to hold an object of type U", and vice versa.
Also, the current specification doesn't seem to specify the type of pv in the cases of allocator_shared,
because pv is merely specified to point some storage instead of an object.
[2024-10-02; will be resolved by issue 3216(i).]
Proposed resolution:
This wording is relative to N4971.
Modify 20.3.2.2.7 [util.smartptr.shared.create] as indicated:
[Drafting note: As the issue submitter prefers option (2), this is wording for that.]
template<class T, ...> shared_ptr<T> make_shared(args); template<class T, class A, ...> shared_ptr<T> allocate_shared(const A& a, args); template<class T, ...> shared_ptr<T> make_shared_for_overwrite(args); template<class T, class A, ...> shared_ptr<T> allocate_shared_for_overwrite(const A& a, args);-2- Preconditions: […]
[…] -7- Remarks:
(7.1) — […]
[…]
(7.5) — When a (sub)object of a non-array type
Uis specified to have an initial value ofv, orU(l...), wherel...is a list of constructor arguments,allocate_sharedshall initialize this (sub)object via the expression
(7.5.1) —
allocator_traits<A2>::construct(a2, pv, v)or(7.5.2) —
allocator_traits<A2>::construct(a2, pv, l...)respectively, where
pvhas typeremove_cv_t<U>*and points to storage suitable to hold an object of typeUanda2of typeA2is a rebound copy of the allocator a passed toallocate_sharedsuch that itsvalue_typeisremove_cv_t<U>.(7.6) — […]
(7.7) — When a (sub)object of non-array type
Uis specified to have a default initial value,allocate_sharedshall initialize this (sub)object via the expressionallocator_traits<A2>::construct(a2, pv), wherepvhas typeremove_cv_t<U>*and points to storage suitable to hold an object of typeUanda2of typeA2is a rebound copy of the allocator a passed toallocate_sharedsuch that itsvalue_typeisremove_cv_t<U>.