Issue 3210: allocate_shared is inconsistent about removing const from the pointer
passed to allocator construct and destroy (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 Resolved status.
3210. allocate_shared is inconsistent about removing const from the pointer passed to allocator construct and destroy
Section: 20.3.2.2.7 [util.smartptr.shared.create] Status: Resolved Submitter: Billy O'Neal III Opened: 2019-05-29 Last modified: 2025-11-11
Priority: 3
View all other issues in [util.smartptr.shared.create].
View all issues with Resolved status.
Discussion:
I implemented the fix for LWG 3008(i) and Stephan pointed out there's an inconsistency here for allocate_shared<const T>.
20.3.2.2.7 [util.smartptr.shared.create] p3 says that the allocator construct call is done without removing_cv_ qualifiers, but 20.3.2.2.7 [util.smartptr.shared.create] p7.12 says that the destroycall is done with removed cv qualifiers.
The fallback for allocator_traits::construct rejects const T* (since it static_caststo 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
constelements somehow, which breaks compatibility with existing allocators unprepared forconstelements here. We would need to extend the allocator requirements to allowconst T*to be passed here, and fix our default toconst_cast. - Fix
allocate_sharedto removeconstbefore callingconstruct, which changes the experience for C++17 customers becauseallocate_sharedconstructs aTinstead of aconst T, but not in a way substantially different to editsP0674 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 P0674while 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 make_shared(args);
template<class T, class A, ...>
shared_ptr allocate_shared(const A& a, args);
template<class T, ...>
shared_ptr make_shared_default_init(args);
template<class T, class A, ...>
shared_ptr 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)or2. (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 typeremove_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).]
[2025-11-11; LWG 3216(i) was accepted in Wrocław. Status changed: New → Resolved.]
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.1) —
2. (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>.
4. (7.6) — […]
5. (7.7) — When a (sub)object of non-array typeUis 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>.