Created on 2020-04-29.00:00:00 last changed yesterday
Proposed resolution:
This wording is relative to N4981.
Modify [specialized.construct] as indicated:
template<class T, class... Args> constexpr T* construct_at(T* location, Args&&... args); namespace ranges { template<class T, class... Args> constexpr T* construct_at(T* location, Args&&... args); }-1- Constraints: is_unbounded_array_v<T> is false. The expression ::new (declval<void*>()) T(declval<Args>()...) is well-formed when treated as an unevaluated operand ([expr.context]).
-?- Mandates: If
is_array_v<T>
is `true`, `sizeof...(Args)` is zero.-2- Effects: Equivalent to:
if constexpr (is_array_v<T>) return ::new (voidify(*location)) T[1](); else return ::new (voidify(*location)) T(std::forward<Args>(args)...);
[ St. Louis 2024-06-24; move to Ready. ]
[ St. Louis 2024-06-24; Jonathan provides improved wording ]
Why not support unbounded arrays, deducing the bound from `sizeof...(Args)`?
JW: There's no motivation to support that here in `construct_at`.
It isn't possible to create unbounded arrays via allocators,
nor via any of the `uninitialized_xxx` algorithms. Extending `construct_at`
that way seems like a design change, not restoring support for something
that used to work with allocators and then got broken in C++20.
Tim observed that the proposed resolution is ill-formed if `T` has an explicit default constructor. Value-initialization would work for that case, and there seems to be little motivation for supplying arguments to initialize the array. In C++17 the `allocator_traits::construct` case only supported value-initialization.
[ 2024-03-18; Jonathan provides new wording ]
During Core review in Varna, Hubert suggested creating `T[1]` for the array case.
This wording is relative to N4971.
Modify [specialized.construct] as indicated:
template<class T, class... Args> constexpr T* construct_at(T* location, Args&&... args); namespace ranges { template<class T, class... Args> constexpr T* construct_at(T* location, Args&&... args); }-1- Constraints: is_unbounded_array_v<T> is false. The expression ::new (declval<void*>()) T(declval<Args>()...) is well-formed when treated as an unevaluated operand ([expr.context]).
-2- Effects: Equivalent to:if constexpr (is_array_v<T>) return ::new (voidify(*location)) T[1]{{std::forward<Args>(args)...}}; else return ::new (voidify(*location)) T(std::forward<Args>(args)...);
[ 2021-12-07; Zhihao Yuan comments and provides improved wording ]
The previous PR allows constructing arbitrary number of elements when T is an array of unknown bound:
extern int a[]; std::construct_at(&a, 0, 1, 2);
and leads to a UB.
This wording is relative to N4901.
Modify [specialized.construct] as indicated:
template<class T, class... Args> constexpr T* construct_at(T* location, Args&&... args); namespace ranges { template<class T, class... Args> constexpr T* construct_at(T* location, Args&&... args); }-1- Constraints: The expression ::new (declval<void*>()) T(declval<Args>()...) is well-formed when treated as an unevaluated operand ([expr.context]) and is_unbounded_array_v<T> is false.
-2- Effects: Equivalent to:returnauto ptr = ::new (voidify(*location)) T(std::forward<Args>(args)...); if constexpr (is_array_v<T>) return launder(location); else return ptr;
[ 2021-01-16; Zhihao Yuan provides wording ]
Previous resolution [SUPERSEDED]:
This wording is relative to N4878.
Modify [specialized.construct] as indicated:
template<class T, class... Args> constexpr T* construct_at(T* location, Args&&... args); namespace ranges { template<class T, class... Args> constexpr T* construct_at(T* location, Args&&... args); }-1- Constraints: The expression ::new (declval<void*>()) T(declval<Args>()...) is well-formed when treated as an unevaluated operand.
-2- Effects: Equivalent to:returnauto ptr = ::new (voidify(*location)) T(std::forward<Args>(args)...); if constexpr (is_array_v<T>) return launder(location); else return ptr;
[ 2020-05-09; Reflector prioritization ]
Set priority to 2 after reflector discussions.
std::construct_at is ill-formed for array types, because the type of the new-expression is T not T* so it cannot be converted to the return type.
In C++17 allocator_traits::construct did work for arrays, because it returns void so there is no ill-formed conversion. On the other hand, in C++17 allocator_traits::destroy didn't work for arrays, because p->~T() isn't valid. In C++20 allocator_traits::destroy does work, because std::destroy_at treats arrays specially, but allocator_traits::construct no longer works because it uses std::construct_at. It seems unnecessary and/or confusing to remove support for arrays in construct when we're adding it in destroy. I suggest that std::construct_at should also handle arrays. It might be reasonable to restrict that support to the case where sizeof...(Args) == 0, if supporting parenthesized aggregate-initialization is not desirable in std::construct_at.History | |||
---|---|---|---|
Date | User | Action | Args |
2024-11-19 16:09:07 | admin | set | status: ready -> voting |
2024-06-24 22:35:27 | admin | set | messages: + msg14212 |
2024-06-24 22:35:27 | admin | set | status: new -> ready |
2024-06-24 17:06:10 | admin | set | messages: + msg14207 |
2024-03-18 16:53:22 | admin | set | messages: + msg14020 |
2021-12-11 14:55:55 | admin | set | messages: + msg12252 |
2021-01-17 15:14:36 | admin | set | messages: + msg11663 |
2021-01-17 15:14:36 | admin | set | messages: + msg11662 |
2020-05-09 19:39:43 | admin | set | messages: + msg11278 |
2020-04-29 00:00:00 | admin | create |