| Doc. no. | P4145R0 |
| Date: | 2026-03-25 |
| Audience: | WG21 |
| Reply to: | Jonathan Wakely <lwgchair@gmail.com> |
is_sufficiently_aligned
T
assert should forbid co_await and co_yield
integral-constant-like
and
constexpr-wrapper-like
exposition-only concept duplication
std::generate and std::ranges::generate wording is unclear for parallel algorithms
flat_set::insert_range specification may be problematic
std::ranges::advance and std::ranges::next when the difference type is also a sentinel type
std::mdspan taking (data_handle_type, mapping_type, accessor_type)
and the corresponding constructor
system_encoded_string() and generic_system_encoded_string() member functions
of std::filesystem::path are misnamed
<simd>
templates
std::basic_string on some platforms
unique_ptr<void>::operator*
is not SFINAE-friendly
std::basic_string's data() and operator[] specification
stable_sort, stable_partition and inplace_merge
variant constructor
hive::splice can throw bad_alloc
operator decltype(auto)" is ill-formed
round_to_nearest" rounding mode is unclear
operator delete should be constexpr
ATOMIC_CHAR8_T_LOCK_FREE
submdspan_extents and submdspan_canonicalize_slices
constant_wrapper wording problems
init in vector_two_norm and matrix_frob_norm
data_member_spec should throw for
cv
-qualified unnamed bit-fields
std::format transcodes for std::wformat_strings
constant_wrapper should assign to value
task
's
final_suspend
should
move
the result
await_transform
needs to use
as_awaitable
task
needs
get_completion_signatures()
task::promise_type::await_transform
declaration and definition mismatch
Section: 16.4.6.9 [reentrancy] Status: Ready Submitter: Stephan T. Lavavej Opened: 2014-07-01 Last modified: 2026-02-20
Priority: 3
View all other issues in [reentrancy].
Discussion:
N3936 16.4.6.9 [reentrancy]/1 talks about "functions", but that doesn't address the scenario of calling different member functions of a single object. Member functions often have to violate and then re-establish invariants. For example, vectors often have "holes" during insertion, and element constructors/destructors/etc. shouldn't be allowed to observe the vector while it's in this invariant-violating state. The [reentrancy] Standardese should be extended to cover member functions, so that implementers can either say that member function reentrancy is universally prohibited, or selectively allowed for very specific scenarios.
(For clarity, this issue has been split off from LWG 2382(i).)[2014-11-03 Urbana]
AJM confirmed with SG1 that they had no special concerns with this issue, and LWG should retain ownership.
AM: this is too overly broad as it also covers calling the exact same member function on a different objectMove to Open
[2015-07 Telecon Urbana]
Marshall to ping STL for updated wording.
[2016-05 email from STL]
I don't have any better suggestions than my original PR at the moment.
Previous resolution [SUPERSEDED]:
This wording is relative to N3936.
Change 16.4.6.9 [reentrancy] p1 as indicated:
-1- Except where explicitly specified in this standard, it is implementation-defined which functions (including different member functions called on a single object) in the Standard C++ library may be recursively reentered.
[2021-07-29 Tim suggests new wording]
The "this pointer" restriction is modeled on 11.9.5 [class.cdtor] p2.
It allows us to continue to specify a member function f as calling some other
member function g, since any such call would use something obtained
from the first member function's this pointer.
const (or are treated as such for the
purposes of data race avoidance). Using "access" means that we also cover direct
access to the object representation, such as the following pathological example
from Arthur O'Dwyer,
which is now undefined:
std::string s = "hello world"; char *first = (char*)&s; char *last = (char*)(&s + 1); s.append(first, last);
Previous resolution [SUPERSEDED]:
This wording is relative to N4892.
Add the following paragraph to 16.4.6.9 [reentrancy]:
-?- During the execution of a standard library non-static member function F on an object, if that object is accessed through a glvalue that is not obtained, directly or indirectly, from the
thispointer of F, in a manner that can conflict (6.10.2.2 [intro.races]) with any access that F is permitted to perform (16.4.6.10 [res.on.data.races]), the behavior is undefined unless otherwise specified.
[2026-02-20; Tim provides new wording]
Refer to "object parameter" not "this pointer".
[2026-02-20; LWG telecon; Status changed: Open → Ready.]
Proposed resolution:
This wording is relative to N5032.
Append the following paragraph to 16.4.6.9 [reentrancy]:
-?- During the execution of a standard library non-static member function F on an object, if that object is accessed through a glvalue that is not obtained, directly or indirectly, from the object parameter of F, in a manner that can conflict (6.10.2.2 [intro.races]) with any access that F is permitted to perform (16.4.6.10 [res.on.data.races]), the behavior is undefined unless otherwise specified.
emplace between optional and variantSection: 22.5.3.4 [optional.assign], 22.6.3.5 [variant.mod], 22.7.4.4 [any.modifiers] Status: Ready Submitter: Richard Smith Opened: 2016-07-13 Last modified: 2025-12-05
Priority: 3
Discussion:
Referring to N4604:
In [optional.object.assign]: emplace (normal form) has a Requires that the construction works.
Requires:
is_constructible_v<T, Args&&...>istrue.
emplace (initializer_list form) has a SFINAE condition:
Remarks: […] unless
is_constructible_v<T, initializer_list<U>&, Args&&...>istrue.
In 22.7.4.4 [any.modifiers]: emplace (normal form) has a Requires that the construction works:
Requires:
is_constructible_v<T, Args...>istrue.
emplace (initializer_list form) has a SFINAE condition:
Remarks: […] unless
is_constructible_v<T, initializer_list<U>&, Args...>istrue.
In 22.6.3.5 [variant.mod]: emplace (T, normal form) has a SFINAE condition:
Remarks: […] unless
is_constructible_v<T, Args...>istrue, andToccurs exactly once inTypes....
emplace (Idx, normal form) has a both a Requires and a SFINAE condition:
Requires:
Remarks: […] unlessI < sizeof...(Types)is_constructible_v<T, Args...>istrue, andToccurs exactly once inTypes....
emplace (T, initializer_list form) has a SFINAE condition:
Remarks: […] unless
is_constructible_v<T, initializer_list<U>&, Args...>istrue, andToccurs exactly once inTypes....
emplace (Idx, initializer_list form) has a both a Requires and a SFINAE condition:
Requires:
Remarks: […] unlessI < sizeof...(Types)is_constructible_v<T, Args...>is true, andToccurs exactly once inTypes....
Why the inconsistency? Should all the cases have a SFINAE requirement?
I see that variant has an additional requirement (T occurs exactly once in Types...), but that
only agues that it must be a SFINAE condition — doesn't say that the other cases (any/variant) should not.
map/multimap/unordered_map/unordered_multimap have SFINAE'd versions of
emplace that don't take initializer_lists, but they don't have any emplace versions
that take ILs.
Suggested resolution:
Add SFINAE requirements to optional::emplace(Args&&... args) and
any::emplace(Args&&... args);
[2016-08 Chicago]
During issue prioritization, people suggested that this might apply to any as well.
Ville notes that 2746(i), 2754(i) and 2756(i) all go together.
[2020-05-10; Daniel comments and provides wording]
The inconsistency between the two any::emplace overloads have been removed by resolving LWG
2754(i) to use Constraints: elements. The last Mandating paper
(P1460R1), adopted in Prague, changed the Requires: elements
for variant::emplace, "I < sizeof...(Types)" to Mandates:, but that paper
was focused on fixing inappropriate preconditions, not searching for consistency here. Given that the
in_place_index_t constructors of variant uses SFINAE-conditions for this form of static
precondition violation, I recommend that its emplace functions use the same style, which would bring
them also in consistency with their corresponding type-based emplace forms that are
Mandates:-free but delegate to the index-based forms.
Previous resolution [SUPERSEDED]:
This wording is relative to N4861.
Modify 22.5.3.4 [optional.assign], as indicated:
template<class... Args> T& emplace(Args&&... args);-29-
[…]MandatesConstraints:is_constructible_v<T, Args...>istrue.template<class U, class... Args> T& emplace(initializer_list<U> il, Args&&... args);-35- Constraints:
[…]is_constructible_v<T, initializer_list<U>&, Args...>istrue.Modify 22.6.3.5 [variant.mod], as indicated:
template<class T, class... Args> T& emplace(Args&&... args);-1- Constraints:
[…]is_constructible_v<T, Args...>istrue, andToccurs exactly once inTypes.template<class T, class U, class... Args> T& emplace(initializer_list<U> il, Args&&... args);-3- Constraints:
[…]is_constructible_v<T, initializer_list<U>&, Args...>istrue, andToccurs exactly once inTypes.template<size_t I, class... Args> variant_alternative_t<I, variant<Types...>>& emplace(Args&&... args);-6- Constraints:
-5- Mandates:I < sizeof...(Types).is_constructible_v<TI, Args...>istrueandI < sizeof...(Types)istrue. […]template<size_t I, class U, class... Args> variant_alternative_t<I, variant<Types...>>& emplace(initializer_list<U> il, Args&&... args);-13- Constraints:
-12- Mandates:I < sizeof...(Types).is_constructible_v<TI, initializer_list<U>&, Args...>istrueandI < sizeof...(Types)istrue. […]
[2025-12-05; Jonathan provides new wording]
There's no need to use SFINAE to check if a variant with N alternatives
allows you to emplace the N+1th alternative.
[2025-12-05; LWG telecon. Moved to Ready]
Proposed resolution:
This wording is relative to N5014.
Modify 22.5.3.4 [optional.assign], as indicated:
template<class... Args> T& emplace(Args&&... args);-29-
[…]MandatesConstraints:is_constructible_v<T, Args...>istrue.template<class U, class... Args> T& emplace(initializer_list<U> il, Args&&... args);-35- Constraints:
[…]is_constructible_v<T, initializer_list<U>&, Args...>istrue.
condition_variable::wait_for is overspecifiedSection: 32.7.4 [thread.condition.condvar] Status: Ready Submitter: Jonathan Wakely Opened: 2020-11-18 Last modified: 2026-02-20
Priority: 3
View all other issues in [thread.condition.condvar].
Discussion:
32.7.4 [thread.condition.condvar] p24 says:
Effects: Equivalent to:
return wait_until(lock, chrono::steady_clock::now() + rel_time);
This is overspecification, removing implementer freedom to make cv.wait_for(duration<float>(1)) work accurately.
steady_clock::now() + duration<float>(n) is time_point<steady_clock,
duration<float, steady_clock::period>>. If the steady clock's period is std::nano
and its epoch is the time the system booted, then in under a second a 32-bit float becomes unable
to exactly represent those time_points! Every second after boot makes duration<float, nano>
less precise.
This means that adding a duration<float> to a time_point (or duration)
measured in nanoseconds is unlikely to produce an accurate value. Either it will round down to a value less
than now(), or round up to one greater than now() + 1s. Either way, the wait_for(rel_time)
doesn't wait for the specified time, and users think the implementation is faulty.
A possible solution is to use steady_clock::now() + ceil<steady_clock::duration>(rel_time)
instead. This converts the relative time to a suitably large integer, and then the addition is not affected
by floating-point rounding errors due to the limited precision of 32-bit float. Libstdc++ has been
doing this for nearly three years. Alternatively, the standard could just say that the relative timeout is
converted to an absolute timeout measured against the steady clock, and leave the details to the implementation.
Some implementations might not be affected by the problem (e.g. if the steady clock measures milliseconds,
or processes only run for a few seconds and use the process start as the steady clock's epoch).
This also affects the other overload of condition_variable::wait_for, and both overloads of
condition_variable_any::wait_for.
[2020-11-29; Reflector prioritization]
Set priority to 3 during reflector discussions.
[2024-06-18; Jonathan adds wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N4981.
Modify 32.7.1 [thread.condition.general] as indicated, adding a new paragraph to the end:
-6- The definitions in 32.7 [thread.condition] make use of the following exposition-only function:template<class Dur> chrono::steady_clock::time_point rel-to-abs(const Dur& rel_time) { return chrono::steady_clock::now() + chrono::ceil<chrono::steady_clock::duration>(rel_time); }Modify 32.7.4 [thread.condition.condvar] as indicated:
template<class Rep, class Period> cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time);-23- Preconditions:
lock.owns_lock()istrueandlock.mutex()is locked by the calling thread, and either [...]-24- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time));[...]
template<class Rep, class Period, class Predicate> cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);-35- Preconditions:
lock.owns_lock()istrueandlock.mutex()is locked by the calling thread, and either [...]-36- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time), std::move(pred));[Note 8: There is no blocking if
pred()is initiallytrue, even if the timeout has already expired. — end note]Modify 32.7.5.2 [thread.condvarany.wait] as indicated:
template<class Lock, class Rep, class Period> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);-11- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time));[...]
template<class Lock, class Rep, class Period, class Predicate> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);-19- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time), std::move(pred));
[2026-02-20; Jonathan provides new wording]
Need to add another change for the interruptible wait_for.
[2026-02-20; LWG telecon; Status changed: New → Ready.]
Proposed resolution:
This wording is relative to N5032.
Modify 32.7.1 [thread.condition.general] as indicated, adding a new paragraph to the end:
-6- The definitions in 32.7 [thread.condition] make use of the following exposition-only function:template<class Dur> chrono::steady_clock::time_point rel-to-abs(const Dur& rel_time) { return chrono::steady_clock::now() + chrono::ceil<chrono::steady_clock::duration>(rel_time); }
Modify 32.7.4 [thread.condition.condvar] as indicated:
template<class Rep, class Period> cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time);-23- Preconditions:
lock.owns_lock()istrueandlock.mutex()is locked by the calling thread, and either [...]-24- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time));[...]
template<class Rep, class Period, class Predicate> cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);-35- Preconditions:
lock.owns_lock()istrueandlock.mutex()is locked by the calling thread, and either [...]-36- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time), std::move(pred));[Note 8: There is no blocking if
pred()is initiallytrue, even if the timeout has already expired. — end note]
Modify 32.7.5.2 [thread.condvarany.wait] as indicated:
template<class Lock, class Rep, class Period> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);-11- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time));[...]
template<class Lock, class Rep, class Period, class Predicate> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);-19- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time), std::move(pred));
Modify 32.7.5.3 [thread.condvarany.intwait] as indicated:
template<class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock, stop_token stoken, const chrono::duration<Rep, Period>& rel_time, Predicate pred);-13- Effects: Equivalent to:
return wait_until(lock, std::move(stoken),chrono::steady_clock::now() + rel_timerel-to-abs(rel_time), std::move(pred));
const overload of lazy_split_view::begin should be constrained by const PatternSection: 25.7.16.2 [range.lazy.split.view] Status: Ready Submitter: Hewill Kang Opened: 2021-09-23 Last modified: 2026-01-16
Priority: 3
View other active issues in [range.lazy.split.view].
View all other issues in [range.lazy.split.view].
Discussion:
Consider the following code snippet:
#include <ranges>
int main() {
auto p = std::views::iota(0)
| std::views::take(1)
| std::views::reverse;
auto r = std::views::single(42)
| std::views::lazy_split(p);
auto f = r.front();
}
r.front() is ill-formed even if r is a forward_range.
This is because the const overload of lazy_split_view::begin is not constrained by the
const Pattern, which makes it still well-formed in such cases. When the const overload
of view_interface<lazy_split_view<V, Pattern>>::front is instantiated, the
subrange{parent_->pattern_} inside lazy_split_view::outer-iterator<true>::operator++()
will cause a hard error since const Pattern is not a range.
[2021-09-24; Daniel comments]
This issue is related to LWG 3592(i).
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
[2026-01-16; LWG telecon. Move to Ready]
Proposed resolution:
This wording is relative to N4892.
Modify 25.7.16.2 [range.lazy.split.view], class template lazy_split_view synopsis, as indicated:
namespace std::ranges { […] template<input_range V, forward_range Pattern> requires view<V> && view<Pattern> && indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> && (forward_range<V> || tiny-range<Pattern>) class lazy_split_view : public view_interface<lazy_split_view<V, Pattern>> { private: […] public: […] constexpr auto begin() { […] } constexpr auto begin() const requires forward_range<V> && forward_range<const V> && forward_range<const Pattern>{ return outer-iterator<true>{*this, ranges::begin(base_)}; } constexpr auto end() requires forward_range<V> && common_range<V> { […] } constexpr auto end() const { if constexpr (forward_range<V> && forward_range<const V> && common_range<const V> && forward_range<const Pattern>) return outer-iterator<true>{*this, ranges::end(base_)}; else return default_sentinel; } }; […] }
is_sufficiently_alignedSection: 20.2.5 [ptr.align] Status: Ready Submitter: Damien Lebrun-Grandie Opened: 2025-07-03 Last modified: 2026-01-30
Priority: 2
View other active issues in [ptr.align].
View all other issues in [ptr.align].
Discussion:
is_sufficiently_aligned should mandate that the alignment template argument is a power of two
and that it is greater equal to the byte alignment of its type template argument.
is_sufficiently_aligned has no Mandates element. It is an
oversight that we realized when implementing P2897R7 into libc++
(in https://github.com/llvm/llvm-project/pull/122603).
The function template was originally proposed as a static member function of the aligned_accessor
class template which has these two Mandates clauses and therefore applied
(see 23.7.3.5.4.1 [mdspan.accessor.aligned.overview] p1). It revision P2897R4,
is_sufficiently_aligned was moved out the class template definition to become the free function
memory helper that was voted into C++26 but the Mandates were lost in the process.
We propose to correct that oversight and reintroduce the following Mandates clauses right above
20.2.5 [ptr.align] p10.
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
The Mandates of Alignment >= alignof(T) was put into
question, as being too restrictive for general purpose free function.
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 20.2.5 [ptr.align] as indicated:
template<size_t Alignment, class T> bool is_sufficiently_aligned(T* ptr);-?- Mandates:
(?.1) —
Alignmentis a power of two, and(?.2) —
Alignment >= alignof(T)istrue.-10- Preconditions:
-11- Returns:ppoints to an objectXof a type similar (7.3.6 [conv.qual]) toT.trueifXhas alignment at leastAlignment, otherwisefalse. -12- Throws: Nothing.
[2026-01-30; Jonathan provides new wording]
[2026-01-30; LWG telecon. Status → Ready.]
Proposed resolution:
This wording is relative to N5032.
Modify 20.2.5 [ptr.align] as indicated:
template<size_t Alignment, class T> bool is_sufficiently_aligned(T* ptr);-?- Mandates:
Alignmentis a power of two.-10- Preconditions:
-11- Returns:ppoints to an objectXof a type similar (7.3.6 [conv.qual]) toT.trueifXhas alignment at leastAlignment, otherwisefalse. -12- Throws: Nothing.
atomic_ref<cv T>::required_alignment should be the same as for TSection: 32.5.7.2 [atomics.ref.ops] Status: New Submitter: Jonathan Wakely Opened: 2025-11-06 Last modified: 2026-02-27
Priority: 2
View other active issues in [atomics.ref.ops].
View all other issues in [atomics.ref.ops].
Discussion:
When P3323R1 fixed support for atomic_ref<cv T>
we didn't consider that an implementation could define required_alignment
to be different from atomic_ref<T>::required_alignment.
For example, a processor could support atomic loads on any int object,
but could require a greater alignment for atomic stores (and read-modify-write
and modify-write operations).
So the required_alignment could be alignof(int) for const int
but 2 * alignof(int) for int, because the latter is needed for stores.
A malicious implementation could even define required_alignment to be
greater for the const T specialization, which would make it undefined
to use the new converting constructor added by P3860R1.
We should constrain implementations to use the same required_alignment
for cv T, so that users can use atomic_ref<const T>
on any object that is referenced by a atomic_ref<T>.
[2026-01-16; Reflector poll.]
Set priority to 2 after reflector poll.
"Should be broaded to include all similar types, not just top-level cv."
"Should we have the same guarantee for is_always_lock_free?
Would allow checking if atomic<volatile T> is usable by checking atomic<T>."
Previous resolution [SUPERSEDED]:
This wording is relative to N5014.
Modify [atomics.refs.ops], as indicated:
static constexpr size_t required_alignment;-1- The alignment required for an object to be referenced by an atomic reference, which is at least
alignof(T). The value ofrequired_alignmentis the same asatomic_ref<remove_cv_t<T>>::required_alignment-2- [Note 1: Hardware could require an object referenced by an
atomic_refto have stricter alignment (6.8.3 [basic.align]) than other objects of typeT. Further, whether operations on anatomic_refare lock-free could depend on the alignment of the referenced object. For example, lock-free operations onstd::complex<double>could be supported only if aligned to2*alignof(double). — end note]
[2026-02-16; Tomasz provides updated resolution.]
Guarantee that required_alignment and is_always_lock_free
values are the same for any similar types.
We define equality to atomic_ref<remove_cv_t<U>,
as volatile atomics may be ill-formed.
Proposed resolution:
This wording is relative to N5032.
Modify [atomics.refs.ops], as indicated:
static constexpr size_t required_alignment;-1- The alignment required for an object to be referenced by an atomic reference, which is at least
alignof(T). For any typeUsimilar toT, the value ofrequired_alignmentis the same asatomic_ref<remove_cv_t<U>>::required_alignment.-2- [Note 1: Hardware could require an object referenced by an
atomic_refto have stricter alignment (6.8.3 [basic.align]) than other objects of typeT. Further, whether operations on anatomic_refare lock-free could depend on the alignment of the referenced object. For example, lock-free operations onstd::complex<double>could be supported only if aligned to2*alignof(double). — end note]static constexpr bool is_always_lock_free;-3- The static data member
is_always_lock_freeistrueif theatomic_reftype's operations are always lock-free, and false otherwise. For any typeUsimilar toTthe value ofis_always_lock_freeis the same asatomic_ref<remove_cv_t<U>>::is_always_lock_free.
assert should forbid co_await and co_yieldSection: 19.3.3 [assertions.assert] Status: Ready Submitter: Jonathan Wakely Opened: 2025-11-06 Last modified: 2026-03-06
Priority: 1
Discussion:
Addresses GB 05-129
The new implementation of assert introduced by P2264R7
might require something like sizeof(bool(__VA_ARGS__)).
Using something like assert((co_yield 1, true)) would be ill-formed
if the implementation of assert uses sizeof, because co_yield cannot
appear in an unevaluated context.
The specification for assert should forbid using any constructs which
are not valid in unevaluated contexts, unless we are certain that the
new assert requirements can be implemented without such tricks using
unevaluated contexts. "Ill-formed; no diagnostic required" seems appropriate.
[2026-03-06 Tim provides wording]
[2026-03-06 LWG telecon; move to Ready]
Proposed resolution:
This wording is relative to N5032.
Modify 19.3.3 [assertions.assert] as indicated:
-3- If
__VA_ARGS__does not expand toana well-formed assignment-expression, the program is ill-formed. If such an assignment-expression is ill-formed when treated as an unevaluated operand (7.6.2.4 [expr.await], 7.6.17 [expr.yield]), the program is ill-formed, no diagnostic required.
Section: 16.4.5.2.1 [namespace.std] Status: Ready Submitter: Tomasz Kamiński Opened: 2025-11-14 Last modified: 2026-01-16
Priority: 2
View other active issues in [namespace.std].
View all other issues in [namespace.std].
Discussion:
The wording in 16.4.5.2.1 [namespace.std] p7 guarantees that reflection of the addressable function can be reliably produced. With the addition of the function parameter name, it is possible to access the parameter name of such functions:
constexpr auto p = parameters_of(^^std::endl)[0]; static_assert( identifier_of(p) == "os" ); // guaranteed?
We should clarify that parameter names used by standard library implementation
remain unspecified, by making result of has_identifier and identifier_of
unspecified on reflection of such parameter.
[2026-01-16; Reflector poll.]
Set priority to 2 after reflector poll.
[2026-01-16; LWG telecon. Move to Ready]
Proposed resolution:
This wording is relative to N5014.
Modify 16.4.5.2.1 [namespace.std], as indicated:
Let F denote a standard library function or function template. Unless F is designated an addressable function, it is unspecified if or how a reflection value designating the associated entity can be formed. For any value
pof typemeta::infothat represents a reflection of a parameter of F, it is unspecified ifhas_identifier(p)returnstrueorfalse, and ifmeta::has_identifier(p)istrue, then theNTMBSproduced bymeta::identifier_of(p)andmeta::u8identifier_of(p)is unspecified.
std::atomic_ref<const T> can be constructed from temporariesSection: 32.5.7 [atomics.ref.generic] Status: Ready Submitter: Jiang An Opened: 2025-11-11 Last modified: 2025-12-05
Priority: Not Prioritized
View all other issues in [atomics.ref.generic].
Discussion:
std::atomic_ref<T> has a constructor taking T&, so when T is a const
but not volatile object type, the constructor parameter can be bound to a temporary expression,
which doesn't seem to make sense. Even after P3860R1, explicitly constructing
std::atomic_ref<const T> from std::atomic_ref<volatile T> can be
well-formed with the undesired semantics when it is well-formed to instantiate
std::atomic_ref<volatile T> and its operator T conversion function, because the
construction calls the conversion function and creates a temporary object. Probably it's better
to disallow such reference binding.
[2025-12-05; LWG telecon. Moved to Ready]
Proposed resolution:
This wording is relative to N5014 after application of P3860R1.
[Drafting note: The deleted overloads were mirrored from the design of
reference_wrapperbefore LWG 2993(i). As these overloads don't participate in implicit conversion, I don't think there will be any similar issue introduced.]
Modify 32.5.7.1 [atomics.ref.generic.general], primary class template atomic_ref synopsis, as indicated:
namespace std {
template<class T> struct atomic_ref {
private:
T* ptr; // exposition only
public:
[…]
constexpr explicit atomic_ref(T&);
explicit atomic_ref(T&&) = delete;
constexpr atomic_ref(const atomic_ref&) noexcept;
template<class U>
constexpr atomic_ref(const atomic_ref<U>&) noexcept;
[…]
};
}
Modify 32.5.7.3 [atomics.ref.int], class template atomic_ref integral-type
specialization synopsis, as indicated:
namespace std {
template<> struct atomic_ref<integral-type> {
private:
integral-type* ptr; // exposition only
public:
[…]
constexpr explicit atomic_ref(integral-type&);
explicit atomic_ref(integral-type&&) = delete;
constexpr atomic_ref(const atomic_ref&) noexcept;
template<class U>
constexpr atomic_ref(const atomic_ref<U>&) noexcept;
[…]
};
}
Modify 32.5.7.4 [atomics.ref.float], class template atomic_ref floating-point-type
specialization synopsis, as indicated:
namespace std {
template<> struct atomic_ref<floating-point-type> {
private:
floating-point-type* ptr; // exposition only
public:
[…]
constexpr explicit atomic_ref(floating-point-type&);
explicit atomic_ref(floating-point-type&&) = delete;
constexpr atomic_ref(const atomic_ref&) noexcept;
template<class U>
constexpr atomic_ref(const atomic_ref<U>&) noexcept;
[…]
};
}
Modify 32.5.7.5 [atomics.ref.pointer], class template atomic_ref pointer-type
specialization synopsis, as indicated:
namespace std {
template<> struct atomic_ref<pointer-type> {
private:
pointer-type* ptr; // exposition only
public:
[…]
constexpr explicit atomic_ref(pointer-type&);
explicit atomic_ref(pointer-type&&) = delete;
constexpr atomic_ref(const atomic_ref&) noexcept;
template<class U>
constexpr atomic_ref(const atomic_ref<U>&) noexcept;
[…]
};
}
integral-constant-like and constexpr-wrapper-like exposition-only concept duplicationSection: 23.7.2.1 [span.syn], 29.10.2 [simd.expos] Status: Ready Submitter: Jonathan Wakely Opened: 2025-12-12 Last modified: 2025-12-12
Priority: Not Prioritized
View all other issues in [span.syn].
Discussion:
Addresses US 151-242NB comment:
We have two exposition-only concepts for similar things (integral-constant-likeandconstant-wrapper-like). The former can be expressed in terms of the latter.
Proposed change: Moveconstant-wrapper-liketo the library introduction and updateintegral-constant-liketo use it.
[2025-12-12; LWG telecon; Status changed: New → Ready.]
Proposed resolution:
This wording is relative to N5014.
Modify 16.3.3.2 [expos.only.entity] by moving the concept (unchanged) from 29.10.2 [simd.expos]:
template<class T, class U = T> using synth-three-way-result = // exposition only decltype(synth-three-way(declval<T&>(), declval<U&>())); template<class T> concept constexpr-wrapper-like = // exposition only convertible_to<T, decltype(T::value)> && equality_comparable_with<T, decltype(T::value)> && bool_constant<T() == T::value>::value && bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value; }
Modify 23.7.2.1 [span.syn] as indicated:
template<class T> concept integral-constant-like = // exposition only is_integral_v<remove_cvref_t<decltype(T::value)>> && !is_same_v<bool, remove_const_t<decltype(T::value)>> && constexpr-wrapper-like<T>;convertible_to<T, decltype(T::value)> && equality_comparable_with<T, decltype(T::value)> && bool_constant<T() == T::value>::value && bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
Modify 29.10.2 [simd.expos] as indicated:
template<class T> concept constexpr-wrapper-like = // exposition only convertible_to<T, decltype(T::value)> && equality_comparable_with<T, decltype(T::value)> && bool_constant<T() == T::value>::value && bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
std::generate and std::ranges::generate wording is unclear for parallel algorithmsSection: 26.7.7 [alg.generate] Status: Ready Submitter: Ruslan Arutyunyan Opened: 2025-12-12 Last modified: 2026-01-30
Priority: 2
Discussion:
std::generate and std::ranges::generate are confusing for parallel algorithms. For both of those
Effects says "Assigns the result of successive evaluations of gen() through each iterator
in the range [first, first + N)." The word "successive" is confusing when we talk about parallelism.
This wording was the preexisting one; P3179 "Parallel Range Algorithms" didn't modify
that, so the problem existed even before.
Generator template
parameter type for C++17 parallel generate. Intel, NVIDIA, and GNU libstdc++ implementations do
multiple copies of Generator object to maximize parallelism, while it's not technically allowed if
I am reading the standard correctly. But without Generator being copyable we have point of contention
and extra synchronization (or we put synchronization part as users' responsibility, which is also not
obvious).
There is no clear solution right away. We could try to fix the wording for both ranges and C++17 parallel
generate but it seems like it requires extra effort because we need to at least verify the new behavior
with LLVM libc++ implementation if we want to make Generator copyable; libc++ currently does not
create per-thread copy of gen object. Perhaps, the best strategy for now is to remove
ranges::generate and ranges::generate_n and return them back in C++29 time frame when we figure
out the proper fix.
[2026-01-16; Reflector poll.]
Set priority to 2 after reflector poll.
[2026-01-30; LWG telecon. Status → Ready.]
Proposed resolution:
This wording is relative to N5032.
Modify 26.4 [algorithm.syn], header <algorithm> synopsis, as indicated:
[…] // 26.7.7 [alg.generate], generate template<class ForwardIterator, class Generator> constexpr void generate(ForwardIterator first, ForwardIterator last, Generator gen); template<class ExecutionPolicy, class ForwardIterator, class Generator> void generate(ExecutionPolicy&& exec, // freestanding-deleted, see 26.3.5 [algorithms.parallel.overloads] ForwardIterator first, ForwardIterator last, Generator gen); template<class OutputIterator, class Size, class Generator> constexpr OutputIterator generate_n(OutputIterator first, Size n, Generator gen); template<class ExecutionPolicy, class ForwardIterator, class Size, class Generator> ForwardIterator generate_n(ExecutionPolicy&& exec, // freestanding-deleted, see 26.3.5 [algorithms.parallel.overloads] ForwardIterator first, Size n, Generator gen); namespace ranges { template<input_or_output_iterator O, sentinel_for<O> S, copy_constructible F> requires invocable<F&> & indirectly_writable<O, invoke_result_t<F&>> constexpr O generate(O first, S last, F gen); template<class R, copy_constructible F> requires invocable<F&> & output_range<R, invoke_result_t<F&>> constexpr borrowed_iterator_t<R> generate(R&& r, F gen); template<input_or_output_iterator O, copy_constructible F> requires invocable<F&> & indirectly_writable<O, invoke_result_t<F&>> constexpr O generate_n(O first, iter_difference_t<O> n, F gen);template<execution-policy Ep, random_access_iterator O, sized_sentinel_for<O> S, copy_constructible F> requires invocable<F&> & indirectly_writable<O, invoke_result_t<F&>> O generate(Ep&& exec, O first, S last, F gen); // freestanding-deleted template<execution-policy Ep, sized-random-access-range R, copy_constructible F> requires invocable<F&> & indirectly_writable<iterator_t<R>, invoke_result_t<F&>> borrowed_iterator_t<R> generate(Ep&& exec, R&& r, F gen); // freestanding-deleted template<execution-policy Ep, random_access_iterator O, copy_constructible F> requires invocable<F&> & indirectly_writable<O, invoke_result_t<F&>> O generate_n(Ep&& exec, O first, iter_difference_t<O> n, F gen); // freestanding-deleted} […]
Modify 26.7.7 [alg.generate] as indicated:
template<class ForwardIterator, class Generator> constexpr void generate(ForwardIterator first, ForwardIterator last, Generator gen); template<class ExecutionPolicy, class ForwardIterator, class Generator> void generate(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last, Generator gen); template<class OutputIterator, class Size, class Generator> constexpr OutputIterator generate_n(OutputIterator first, Size n, Generator gen); template<class ExecutionPolicy, class ForwardIterator, class Size, class Generator> ForwardIterator generate_n(ExecutionPolicy&& exec, ForwardIterator first, Size n, Generator gen); template<input_or_output_iterator O, sentinel_for<O> S, copy_constructible F> requires invocable<F&> & indirectly_writable<O, invoke_result_t<F&>> constexpr O ranges::generate(O first, S last, F gen); template<class R, copy_constructible F> requires invocable<F&> & output_range<R, invoke_result_t<F&>> constexpr borrowed_iterator_t<R> ranges::generate(R&& r, F gen); template<input_or_output_iterator O, copy_constructible F> requires invocable<F&> & indirectly_writable<O, invoke_result_t<F&>> constexpr O ranges::generate_n(O first, iter_difference_t<O> n, F gen);template<execution-policy Ep, random_access_iterator O, sized_sentinel_for<O> S, copy_constructible F> requires invocable<F&> & indirectly_writable<O, invoke_result_t<F&>> O ranges::generate(Ep&& exec, O first, S last, F gen); template<execution-policy Ep, sized-random-access-range R, copy_constructible F> requires invocable<F&> & indirectly_writable<iterator_t<R>, invoke_result_t<F&>> borrowed_iterator_t<R> ranges::generate(Ep&& exec, R&& r, F gen); template<execution-policy Ep, random_access_iterator O, copy_constructible F> requires invocable<F&> & indirectly_writable<O, invoke_result_t<F&>> O ranges::generate_n(Ep&& exec, O first, iter_difference_t<O> n, F gen);-1- Let
[…]Nbemax(0, n)for thegenerate_nalgorithms, andlast - firstfor thegeneratealgorithms.
Section: 21.4 [meta.reflection] Status: Ready Submitter: Daniel Katz Opened: 2025-12-11 Last modified: 2026-02-20
Priority: 2
Discussion:
Discussion on the Core mailing list surfaced a handful of places in 21.4 [meta.reflection] that use the "precedes" relation (defined in 6.5 [basic.lookup] and primarily used for name lookup) when the "reachable" relation (defined in 10.7 [module.reach]) is really what we want.
[2026-02-18; Reflector poll.]
Set priority to 2 after reflector poll.
[2026-02-20; LWG telecon; Status changed: New → Ready.]
Proposed resolution:
This wording is relative to N5032.
Modify 21.4.6 [meta.reflection.names] as indicated:
consteval bool has_identifier(info r);-1- Returns:
(1.1) — […]
[…]
(1.8) — Otherwise, if
rrepresents the parameterPof a functionF, then letSbe the set of declarations, ignoring any explicit instantiations, thatprecede someare reachable from a point in the evaluation context and that declare eitherFor a templated function of whichFis a specialization; […][…]
consteval string_view identifier_of(info r); consteval u8string_view u8identifier_of(info r);-2- Let
-3- Returns: An ntmbs, encoded withEbe UTF-8 foru8identifier_of, and otherwise the ordinary literal encoding.E, determined as follows:
(3.1) — […]
(3.2) — […]
(3.3) — Otherwise, if
rrepresents the parameterPof a functionF, then letSbe the set of declarations, ignoring any explicit instantiations, thatprecede someare reachable from a point in the evaluation context and that declare eitherFor a templated function of whichFis a specialization; the name that was introduced by a declaration inSfor the parameter corresponding toP.[…]
Modify 21.4.7 [meta.reflection.queries] as indicated:
consteval info type_of(info r);[…]-2- Returns:
(2.1) — […]
[…]
(2.4) — Otherwise, if
rrepresents an enumeratorNof an enumerationE, then:
(2.4.1) — If
Eis defined by a declarationDthatprecedesis reachable from a pointPin the evaluation context andPdoes not occur within an enum-specifier ofD, then a reflection ofE.(2.4.2) — Otherwise, a reflection of the type of
Nprior to the closing brace of the enum-specifier as specified in 9.8.1 [dcl.enum].[…]
consteval bool has_default_argument(info r);-41- Returns: If
rrepresents a parameterPof a functionF, then:
(41.1) — If
Fis a specialization of a templated functionT, thentrueif there exists a declarationDofTthatprecedes someis reachable from a point in the evaluation context andDspecifies a default argument for the parameter ofTcorresponding toP. Otherwise,false.(41.2) — Otherwise, if there exists a declaration
DofFthatprecedes someis reachable from a point in the evaluation context andDspecifies a default argument forP, thentrue.
Modify 21.4.18 [meta.reflection.annotation] as indicated:
consteval vector<info> annotations_of(info item);-1- Let
-2- Returns: AEbe […]vectorcontaining all of the reflectionsRrepresenting each annotation applying to each declaration ofEthatprecedesis reachable from eithersomea point in the evaluation context (7.7 [expr.const]) or a point immediately following the class-specifier of the outermost class for which such a point is in a complete-class context. […]
flat_set::insert_range specification may be problematicSection: 23.6.11.5 [flat.set.modifiers], 23.6.12.5 [flat.multiset.modifiers] Status: Ready Submitter: Hewill Kang Opened: 2025-12-22 Last modified: 2026-02-20
Priority: 2
Discussion:
The function adds elements via:
ranges::for_each(rg, [&](auto&& e) {
c.insert(c.end(), std::forward<decltype(e)>(e));
});
Here, e is an element of the input range.
e can also be converted to initializer_list,
as vector::insert has an overload of insert(const_iterator, initializer_list<T>).
[2026-02-18; Reflector poll.]
Set priority to 2 after reflector poll.
Pessimizing a very common case (matching type), for uncommon one.
[2026-02-20; LWG telecon; Status changed: New → Ready.]
Implementations can optimize away the extra move for the case where the range's value type is already the same as the flat set's value type.
Proposed resolution:
This wording is relative to N5032.
Modify 23.6.11.5 [flat.set.modifiers] as indicated:
template<container-compatible-range<value_type> R> constexpr void insert_range(R&& rg);-10- Effects: Adds elements to
cas if by:ranges::for_each(rg, [&](value_typeauto&&e) { c.insert(c.end(), std::movestd::forward<decltype(e)>(e)); });
Modify 23.6.12.5 [flat.multiset.modifiers] as indicated:
template<container-compatible-range<value_type> R> void insert_range(R&& rg);-9- Effects: Adds elements to
cas if by:ranges::for_each(rg, [&](value_typeauto&&e) { c.insert(c.end(), std::movestd::forward<decltype(e)>(e)); });
std::ranges::advance and std::ranges::next when the difference type is also a sentinel typeSection: 24.4.4.2 [range.iter.op.advance], 24.4.4.4 [range.iter.op.next] Status: Ready Submitter: Jiang An Opened: 2026-01-09 Last modified: 2026-02-27
Priority: 3
View all other issues in [range.iter.op.advance].
Discussion:
Currently, ranges::advance and ranges::next have operator() overloads that accept
(iterator, sentinel) and (iterator, difference). However, when the difference type
of the iterator is also a sentinel type, there may be ambiguity in both overloads.
#include <cstddef>
#include <iterator>
#include <type_traits>
template<class T>
struct FwdIter { // triggers ADL for T
FwdIter();
using value_type = std::remove_cv_t<T>;
using difference_type = int;
T& operator*() const;
FwdIter& operator++();
FwdIter operator++(int);
friend bool operator==(const FwdIter&, const FwdIter&);
};
static_assert(std::forward_iterator<FwdIter<int>>);
struct OmniConv {
OmniConv(const auto&);
friend bool operator==(OmniConv, OmniConv); // found by ADL via things related to OmniConv
};
int main() {
FwdIter<OmniConv> it{};
std::ranges::advance(it, 0); // ambiguous
std::ranges::next(it, 0); // ambiguous
}
Perhaps it would be better to ensure that calling ranges::advance or ranges::next
with an iterator value and a value of the difference type is unambiguous. Note that
wrapping iterators that trigger ADL for the value type like the FwdIter in this example
are common in standard library implementations, so ambiguity can be easily raised from
containers with such OmniConv being their element type.
[2026-02-20; Reflector poll.]
Set priority to 3 after reflector poll.
The type in the example is basically a std::any that additionally type-erases
equality. That causes a problem for templated iterators that have their
value type's namespace as an associated namespace for ADL.
Instead of allowing integers to be sentinels in general but just restricting
them from being used with these overloads, we could just change the concept
so that integers do not model sentinel_for.
That's similar to what we did for span's constructor which has a similar
problem (though that one's more aggressive due to compatibility constraints).
Previous resolution [SUPERSEDED]:
This wording is relative to N5032.
Modify 24.2 [iterator.synopsis], header
<iterator>synopsis, as indicated:[…] namespace std { template<class T> using with-reference = T&; // exposition only template<class T> concept can-reference // exposition only = requires { typename with-reference<T>; }; template<class T> concept dereferenceable // exposition only = requires(T& t) { { *t } -> can-reference; // not required to be equality-preserving }; template<class T> constexpr bool is-integer-like = see below; // exposition only […] // 24.4.4 [range.iter.ops], range iterator operations namespace ranges { // 24.4.4.2 [range.iter.op.advance], ranges::advance template<input_or_output_iterator I> constexpr void advance(I& i, iter_difference_t<I> n); // freestanding template<input_or_output_iterator I,sentinel_for<I>class S> requires (!is-integer-like<S>) && sentinel_for<S, I> constexpr void advance(I& i, S bound); // freestanding template<input_or_output_iterator I, sentinel_for<I> S> constexpr iter_difference_t<I> advance(I& i, iter_difference_t<I> n, // freestanding S bound); […] // 24.4.4.4 [range.iter.op.next], ranges::next template<input_or_output_iterator I> constexpr I next(I x); // freestanding template<input_or_output_iterator I> constexpr I next(I x, iter_difference_t<I> n); // freestanding template<input_or_output_iterator I,sentinel_for<I>class S> requires (!is-integer-like<S>) && sentinel_for<S, I> constexpr I next(I x, S bound); // freestanding template<input_or_output_iterator I, sentinel_for<I> S> constexpr I next(I x, iter_difference_t<I> n, S bound); // freestanding } […] }Modify 24.4.4.2 [range.iter.op.advance] as indicated:
template<input_or_output_iterator I,sentinel_for<I>class S> requires (!is-integer-like<S>) && sentinel_for<S, I> constexpr void advance(I& i, S bound);-3- Preconditions: […]
Modify 24.4.4.4 [range.iter.op.next] as indicated:
template<input_or_output_iterator I,sentinel_for<I>class S> requires (!is-integer-like<S>) && sentinel_for<S, I> constexpr I next(I x, S bound);-3- Effects: […]
[2026-02-20; Jonathan provides new wording]
[2026-02-27; LWG telecon: move to Ready]
Proposed resolution:
This wording is relative to N5032.
Modify 24.3.4.7 [iterator.concept.sentinel] as indicated:
template<class S, class I>
concept sentinel_for =
semiregular<S> &&
!is-integer-like<S> &&
input_or_output_iterator<I> &&
weakly-equality-comparable-with<S, I>; // see 18.5.4 [concept.equalitycomparable]
std::mdspan taking (data_handle_type, mapping_type, accessor_type)
and the corresponding constructorSection: 23.7.3.6.1 [mdspan.mdspan.overview] Status: Ready Submitter: Jiang An Opened: 2026-01-09 Last modified: 2026-02-20
Priority: Not Prioritized
View other active issues in [mdspan.mdspan.overview].
View all other issues in [mdspan.mdspan.overview].
Discussion:
Currently, the following deduction guide of std::mdspan takes the data handle by reference:
template<class MappingType, class AccessorType>
mdspan(const typename AccessorType::data_handle_type&, const MappingType&,
const AccessorType&)
-> mdspan<typename AccessorType::element_type, typename MappingType::extents_type,
typename MappingType::layout_type, AccessorType>;
But the corresponding constructor takes the data handle by value:
constexpr mdspan(data_handle_type p, const mapping_type& m, const accessor_type& a);
The distinction is observable with volatile glvalues. E.g., in the following example,
CTAD fails but explicitly specifying template arguments works
(demo):
#include <cstddef>
#include <mdspan>
int main() {
int a[1]{};
int * volatile p = a;
std::mdspan(
p,
std::layout_right::mapping<std::extents<std::size_t, 1>>{},
std::default_accessor<int>{}); // error (but accidentally accepted by libc++)
std::mdspan<int, std::extents<std::size_t, 1>>(
p,
std::layout_left::mapping<std::extents<std::size_t, 1>>{},
std::default_accessor<int>{}); // OK
}
Given we're generally passing data handle by value, it seems better to remove const & from
const typename AccessorType::data_handle_type& in the deduction guide, which is more
consistent with the constructor and accept more seemingly valid uses.
&,
see llvm/llvm-project#175024.
[2026-02-20; LWG telecon; Status changed: New → Ready.]
Proposed resolution:
This wording is relative to N5032.
Modify 23.7.3.6.1 [mdspan.mdspan.overview], class template mdspan synopsis, as indicated:
namespace std {
[…]
template<class MappingType, class AccessorType>
mdspan(const typename AccessorType::data_handle_type&, const MappingType&,
const AccessorType&)
-> mdspan<typename AccessorType::element_type, typename MappingType::extents_type,
typename MappingType::layout_type, AccessorType>;
}
system_encoded_string() and generic_system_encoded_string() member functions
of std::filesystem::path are misnamedSection: 31.12.6.1 [fs.class.path.general], 31.12.6.3.2 [fs.path.type.cvt], 31.12.6.5.6 [fs.path.native.obs], 31.12.6.5.7 [fs.path.generic.obs], D.22.2 [depr.fs.path.obs] Status: Ready Submitter: Tom Honermann Opened: 2026-01-11 Last modified: 2026-02-20
Priority: Not Prioritized
Discussion:
Addresses US 189-304
Thesystem_encoded_string() and generic_system_encoded_string() member functions of
std::filesystem::path are misnamed. These functions were added as renamed versions of
string() and generic_string() respectively by P2319R5 ("Prevent path
presentation problems"). The original function names are now deprecated.
The C++ standard does not define "system encoding" and casual use of such a term is at best
ambiguous. In common language, one might expect the "system encoding" to correspond to the
environment encoding (for which std::text_encoding::environment() provides a definition)
or the encoding of the current locale. However, neither of these encodings corresponds to
the encoding these functions are expected to use. 31.12.6.3.2 [fs.path.type.cvt] defines
"native encoding" and, for char-based filesystem paths, states (in a note) that
"For Windows-based operating systems, the native ordinary encoding is determined by calling
a Windows API function." It is not specified which Windows API function is consulted, but
existing implementations call AreFileApisANSI() to choose between the Active Code Page
(CP_ACP) and the OEM Code Page (CP_OEMCP). Microsoft's implementation also checks for
a thread-local locale and, if the locale encoding is UTF-8, prefers that (CP_UTF8) over
either of the other two.
[2026-02-20; LWG telecon; Status changed: New → Ready.]
Proposed resolution:
This wording is relative to N5032.
Modify 31.12.6.1 [fs.class.path.general], class path synopsis, as indicated:
namespace std::filesystem {
class path {
public:
[…]
// 31.12.6.5.6 [fs.path.native.obs], native format observers
[…]
std::string display_string() const;
std::string systemnative_encoded_string() const;
std::wstring wstring() const;
[…]
// 31.12.6.5.7 [fs.path.generic.obs], generic format observers
[…]
std::string generic_display_string() const;
std::string generic_systemnative_encoded_string() const;
std::wstring generic_wstring() const;
[…]
};
}
Modify 31.12.6.3.2 [fs.path.type.cvt] as indicated:
-2- For member function arguments that take character sequences representing paths and for member functions returning strings, value type and encoding conversion is performed if the value type of the argument or return value differs from
path::value_type. For the argument or return value, the method of conversion and the encoding to be converted to is determined by its value type:
(2.1) —
[Note 1: For POSIX-based operating systemschar: The encoding is the native ordinary encoding. The method of conversion, if any, is operating system dependent.path::value_typeischarso no conversion fromcharvalue type arguments or tocharvalue type return values is performed. For Windows-based operating systems, the native ordinary encoding is determined bycalling a Windows API functionthe current locale encoding and the WindowsAreFileApisANSIfunction. — end note] […](2.2) — […]
[…]
Modify 31.12.6.5.6 [fs.path.native.obs] as indicated:
std::stringsystemnative_encoded_string() const; std::wstring wstring() const; std::u8string u8string() const; std::u16string u16string() const; std::u32string u32string() const;-7- Returns:
-8- Remarks: Conversion, if any, is performed as specified by 31.12.6.3 [fs.path.cvt].native().
Modify 31.12.6.5.7 [fs.path.generic.obs] as indicated:
std::string generic_systemnative_encoded_string() const; std::wstring generic_wstring() const; std::u8string generic_u8string() const; std::u16string generic_u16string() const; std::u32string generic_u32string() const;-5- Returns: The pathname in the generic format.
-6- Remarks: Conversion, if any, is specified by 31.12.6.3 [fs.path.cvt].
Modify D.22.2 [depr.fs.path.obs] as indicated:
std::string string() const;-2- Returns:
.systemnative_encoded_string()std::string generic_string() const;-3- Returns:
generic_.systemnative_encoded_string()
<simd> templatesSection: 29.10.1 [simd.general] Status: Ready Submitter: Tim Song Opened: 2026-03-05 Last modified: 2026-03-06
Priority: Not Prioritized
Discussion:
Addresses US 176-280.
It does not appear that any of the class templates in <simd> can be profitably specialized by the user – or that the non-member functions can be used with such user specializations.
[2026-03-06 LWG telecon; move to Ready]
Proposed resolution:
This wording is relative to N5032.
Add the following paragraph to the end of 29.10.1 [simd.general]:
-?- If a program declares an explicit or partial specialization of any of the templates specified in subclause 29.10 [simd], the program is ill-formed, no diagnostic required.
Strike 29.10.4 [simd.traits] p3 as redundant:
-3- The behavior of a program that adds specializations foralignmentis undefined.
Section: 21.3.6.4 [meta.unary.prop], 21.3.8 [meta.rel] Status: Ready Submitter: Tim Song Opened: 2026-03-05 Last modified: 2026-03-06
Priority: Not Prioritized
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
Discussion:
Per 7.7 [expr.const]p23, calls to immediate functions are not immediate invocations,
and therefore are not required to result in constant expressions, if the call occurs in an unevaluated operand.
Currently, some type traits (e.g. is_assignable, is_invocable) specify that the construct at issue is treated as an unevaluated operand,
while others (is_constructible, is_convertible) do not. This seems like a bad state of affairs.
requires expressions
(and implementations). This arguably makes the traits less useful in some contexts where false negatives are tolerable
but false positives are not.
[2026-03-06 LWG telecon; move to Ready]
Proposed resolution:
This wording is relative to N5032.
Modify [tab:meta.unary.prop], Table 54 — Type property predicates as indicated:
Template Condition Preconditions … … … template<class T, class U> struct reference_constructs_from_temporary;Tis a reference type, and the initializationT t(VAL<U>);is well-formed and bindstto a temporary object whose lifetime is extended (6.8.7 [class.temporary]). The full-expression of the variable initialization is treated as an unevaluated operand (7.2.3 [expr.context]). Access checking is performed as if in a context unrelated toTandU. Only the validity of the immediate context of the variable initialization is considered. [Note 5: … — end note][…] template<class T, class U> struct reference_converts_from_temporary;Tis a reference type, and the initializationT t = VAL<U>;is well-formed and bindstto a temporary object whose lifetime is extended (6.8.7 [class.temporary]). The full-expression of the variable initialization is treated as an unevaluated operand (7.2.3 [expr.context]). Access checking is performed as if in a context unrelated toTandU. Only the validity of the immediate context of the variable initialization is considered. [Note 6: … — end note][…] … … …
Modify 21.3.6.4 [meta.unary.prop] p9 as indicated:
-9- The predicate condition for a template specialization
is_constructible<T, Args...>shall be satisfied if and only if the following variable definition would be well-formed for some invented variablet:T t(declval<Args>()...);[Note 7: … — end note]
The full-expression of the variable initialization is treated as an unevaluated operand (7.2.3 [expr.context]). Access checking is performed as if in a context unrelated toTand any of theArgs. Only the validity of the immediate context of the variable initialization is considered. [Note 8: … — end note]
Modify 21.3.8 [meta.rel] p6 as indicated:
-9- The predicate condition for a template specialization
is_convertible<From, To>shall be satisfied if and only if the returnexpressionstatement (8.8.4 [stmt.return]) in the following code would be well-formed, including any implicit conversions to the return type of the function:To test() { return declval<From>(); }[Note 4: … — end note]
Access checking is performed in a context unrelated toToandFrom. The operand of thereturnstatement (including initialization of the returned object or reference, if any) is treated as an unevaluated operand (7.2.3 [expr.context]), and only the validity of its immediate context is considered.Only the validity of the immediate context of the expression of the[Note 5: … — end note]returnstatement (8.8.4 [stmt.return]) (including initialization of the returned object or reference) is considered.
year is ambiguousSection: 30.12 [time.format], 30.13 [time.parse] Status: Tentatively Ready Submitter: Matt Stephanson Opened: 2022-11-18 Last modified: 2025-12-04
Priority: 3
View other active issues in [time.format].
View all other issues in [time.format].
Discussion:
An issue has been identified regarding the two-digit formatting of negative years according to Table [tab:time.format.spec] (30.12 [time.format]):
cout << format("{:%y} ", 1976y) // "76"
<< format("{:%y}", -1976y); // also "76"?
The relevant wording is
The last two decimal digits of the year. If the result is a single digit it is prefixed by
0. The modified command%Oyproduces the locale's alternative representation. The modified command%Eyproduces the locale's alternative representation of offset from%EC(year only).
MSVC STL treats the regular modified form symmetrically. Just as %Ey is the offset from
%EC, so %y is the offset from %C, which is itself "[t]he year divided by 100
using floored division." (emphasis added). Because -1976 is the 24th year of the -20th century,
the above code will print "76 24" using MSVC STL. However, many users expect, and
libc++
gives, a result based on the literal wording, "76 76".
%C and %y as the quotient and remainder
of floored division by 100.
Howard Hinnant, coauthor of the original 30.12 [time.format] wording in P0355 adds:
On the motivation for this design it is important to remember a few things:
POSIX
strftime/strptimedoesn't handle negative years in this department, so this is an opportunity for an extension in functionality.This is a formatting/parsing issue, as opposed to a computational issue. This means that human readability of the string syntax is the most important aspect. Computational simplicity takes a back seat (within reason).
%Ccan't be truncated division, otherwise the years [-99, -1] would map to the same century as the years [0, 99]. So floored division is a pretty easy and obvious solution.
%yis obvious for non-negative years: The last two decimal digits, ory % 100.This leaves how to represent negative years with
%y. I can think of 3 options:
Use the last two digits without negating: -1976 → 76.
Use the last two digits and negate it: -1976 → -76.
Use floored modulus arithmetic: -1976 → 24.
The algorithm to convert
I discounted solution 3 as not sufficiently obvious. If the output for -1976 was 23, the human reader wouldn't immediately know that this is off by 1. The reader is expecting the POSIX spec:%Cand%yinto a year is not important to the client because these are both strings, not integers. The client will do it withparse, not100*C + y.the last two digits of the year as a decimal number [00,99].
24 just doesn't cut it.
That leaves solution 1 or 2. I discounted solution 2 because having the negative in 2 places (the%Cand%y) seemed overly complicated and more error prone. The negative sign need only be in one place, and it has to be in%Cto prevent ambiguity. That leaves solution 1. I believe this is the solution for an extension of the POSIX spec to negative years with the property of least surprise to the client. The only surprise is in%C, not%y, and the surprise in%Cseems unavoidable.
[2022-11-30; Reflector poll]
Set priority to 3 after reflector poll.
A few votes for priority 2. Might need to go to LEWG.
Previous resolution [SUPERSEDED]:
This wording is relative to N4917.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A: This is Howard Hinnant's choice (3)
Modify 30.12 [time.format], Table [tab:time.format.spec] as indicated:
Table 102 — Meaning of conversion specifiers [tab:time.format.spec] Specifier Replacement […]%yThe last two decimal digits of the yearremainder after dividing the year by 100 using floored division.
If the result is a single digit it is prefixed by0.
The modified command%Oyproduces the locale's alternative representation. The
modified command%Eyproduces the locale's alternative representation of offset from
%EC(year only).[…]Modify 30.13 [time.parse], Table [tab:time.parse.spec] as indicated:
Table 103 — Meaning of parseflags [tab:time.parse.spec]Flag Parsed value […]%yThe last two decimal digits of the yearremainder after dividing the year by 100 using floored division.
If the century is not otherwise specified (e.g.
with%C), values in the range [69,99] are presumed to refer to the years 1969 to 1999,
and values in the range [00,68] are presumed to refer to the years 2000 to 2068. The
modified command%N yspecifies the maximum number of characters to read. If N is
not specified, the default is 2. Leading zeroes are permitted but not required. The
modified commands%Eyand%Oyinterpret the locale's alternative representation.[…]Option B: This is Howard Hinnant's choice (1)
Modify 30.12 [time.format], Table [tab:time.format.spec] as indicated:
Table 102 — Meaning of conversion specifiers [tab:time.format.spec] Specifier Replacement […]%yThe last two decimal digits of the year, regardless of the sign of the year.
If the result is a single digit it is prefixed by0.
The modified command%Oyproduces the locale's alternative representation. The
modified command%Eyproduces the locale's alternative representation of offset from
%EC(year only).
[Example ?:cout << format("{:%C %y}", -1976y);prints-20 76. — end example][…]Modify 30.13 [time.parse], Table [tab:time.parse.spec] as indicated:
Table 103 — Meaning of parseflags [tab:time.parse.spec]Flag Parsed value […]%yThe last two decimal digits of the year, regardless of the sign of the year.
If the century is not otherwise specified (e.g.
with%C), values in the range [69,99] are presumed to refer to the years 1969 to 1999,
and values in the range [00,68] are presumed to refer to the years 2000 to 2068. The
modified command%N yspecifies the maximum number of characters to read. If N is
not specified, the default is 2. Leading zeroes are permitted but not required. The
modified commands%Eyand%Oyinterpret the locale's alternative representation.
[Example ?:year y; istringstream{"-20 76"} >> parse("%3C %y", y);results in
y == -1976y. — end example][…]
[2025-10-17; Jonathan provides updated wording using Option B]
[2025-12-04; Reflector poll.]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 30.12 [time.format], Table [tab:time.format.spec] as indicated:
Table 133 — Meaning of conversion specifiers [tab:time.format.spec] Specifier Replacement […]%yThe last two decimal digits of the year, regardless of the sign of the year.
If the result is a single digit it is prefixed by0.
The modified command%Oyproduces the locale's alternative representation. The
modified command%Eyproduces the locale's alternative representation of offset from
%EC(year only).
[Example ?:cout << format("{:%C %y}", -1976y);prints-20 76. — end example][…]
Modify 30.13 [time.parse], Table [tab:time.parse.spec] as indicated:
Table 103 — Meaning of parseflags [tab:time.parse.spec]Flag Parsed value […]%yThe last two decimal digits of the year, regardless of the sign of the year.
If the century is not otherwise specified (e.g.
with%C), values in the range [69,99] are presumed to refer to the years 1969 to 1999,
and values in the range [00,68] are presumed to refer to the years 2000 to 2068. The
modified command%N yspecifies the maximum number of characters to read. If N is
not specified, the default is 2. Leading zeroes are permitted but not required. The
modified commands%Eyand%Oyinterpret the locale's alternative representation.
[Example ?:year y; istringstream{"-20 76"} >> parse("%3C %y", y);results in
y == -1976y. — end example][…]
std::formatSection: 28.5.2.2 [format.string.std] Status: Tentatively Ready Submitter: Jens Maurer Opened: 2024-04-30 Last modified: 2026-01-19
Priority: 3
View other active issues in [format.string.std].
View all other issues in [format.string.std].
Discussion:
There are std::format variants that take an explicit std::locale parameter.
There is the "L" format specifier that uses that locale (or some environment
locale) for formatting, according to 28.5.2.2 [format.string.std] p17:
"For integral types, the locale-specific form causes the context's locale to be used to insert the appropriate digit group separator characters."
It is unclear which specific facets are used to make this happen. This is important, because users can install their own facets into a given locale. Specific questions include:
Is num_put<> being used? Or just numpunct<>?
Are any of the _byname facets being used?
Assuming the encoding for char is UTF-8, the use of a user-provided
num_put<> facet (as opposed to std::format creating the output based on
numpunct<>) would allow digit separators that are not expressibly as a
single UTF-8 code unit.
[2024-05-08; Reflector poll]
Set priority to 3 after reflector poll.
[2024-06-12; SG16 meeting]
The three major implementations all use numpunct but not num_put,
clarify that this is the intended behaviour.
[2025-06-12; Jonathan provides wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 28.5.2.2 [format.string.std] as indicated:
-17- When theLoption is used, the form used for the conversion is called the locale-specific form. TheLoption is only valid for arithmetic types, and its effect depends upon the type.
- (17.1) — For integral types, the locale-specific form causes the context’s locale to be used to insert the appropriate digit group separator characters as if obtained with
numpunct<charT>::groupingandnumpunct<charT>::thousands_sep.- (17.2) — For floating-point types, the locale-specific form causes the context’s locale to be used to insert the appropriate digit group and radix separator characters as if obtained with
numpunct<charT>::grouping,numpunct<charT>::thousands_sep, andnumpunct<charT>::decimal_point.- (17.3) — For the textual representation of
bool, the locale-specific form causes the context’s locale to be used to insert the appropriate string as if obtained withnumpunct<charT>::truenameornumpunct<charT>::falsename.
[2025-08-27; SG16 meeting]
SG16 unanimously approved new wording from Victor. The new wording incorporates similar wording as added by P2419R2 to address 3565(i). Status updated SG16 → Open.
[2026-01-16; Reflector poll.]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 28.5.2.2 [format.string.std] as indicated:
-17- When theLoption is used, the form used for the conversion is called the locale-specific form. TheLoption is only valid for arithmetic types, and its effect depends upon the type.If the string literal encoding is a Unicode encoding form and the locale is among an implementation-defined set of locales, each replacement that depends on the locale is performed as if the replacement character sequence is converted to the string literal encoding.
- (17.1) — For integral types, the locale-specific form causes the context’s locale to be used to insert the appropriate digit group separator characters as if obtained with
numpunct<charT>::groupingandnumpunct<charT>::thousands_sep.- (17.2) — For floating-point types, the locale-specific form causes the context’s locale to be used to insert the appropriate digit group and radix separator characters as if obtained with
numpunct<charT>::grouping,numpunct<charT>::thousands_sep, andnumpunct<charT>::decimal_point.- (17.3) — For the textual representation of
bool, the locale-specific form causes the context’s locale to be used to insert the appropriate string as if obtained withnumpunct<charT>::truenameornumpunct<charT>::falsename.
std::launder might be overly strictSection: 17.6.5 [ptr.launder] Status: Tentatively Ready Submitter: Jiang An Opened: 2024-07-30 Last modified: 2026-02-13
Priority: 3
View all other issues in [ptr.launder].
Discussion:
From issue cplusplus/draft#4553 which is considered non-editorial.
Currently,std::launder has a precondition that the pointed to object must be within
its lifetime. However, per the example added by P0593R6, it's probably intended
that the result of std::launder should be allowed to point to an array element
subobject whose lifetime has not started yet.
[2024-10-02; Reflector poll]
Set priority to 3 after reflector poll. Needs review by Core.
[Wrocław 2024-11-18; approved by Core]
[2026-02-13; Reflector poll.]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4986.
Modify 17.6.5 [ptr.launder] as indicated:
template<class T> [[nodiscard]] constexpr T* launder(T* p) noexcept;-1- Mandates:
-2- Preconditions:!is_function_v<T> && !is_void_v<T>istrue.prepresents the address A of a byte in memory. An object Xthat is within its lifetime (6.8.4 [basic.life]) andwhose type is similar (7.3.6 [conv.qual]) toTis located at the address A, and is either within its lifetime (6.8.4 [basic.life]) or is an array element subobject whose containing array object is within its lifetime. All bytes of storage that would be reachable through (6.9.4 [basic.compound]) the result are reachable throughp. -3- Returns: A value of typeT*that points to X. […]
std::basic_string on some platformsSection: 27.4.3.8.2 [string.find] Status: Tentatively Ready Submitter: Jiang An Opened: 2025-05-05 Last modified: 2025-12-04
Priority: 3
View other active issues in [string.find].
View all other issues in [string.find].
Discussion:
P1148R0 respecified the searching functions of std::basic_string to return
corresponding string view type's npos member constant (equal to std::size_t(-1)), converted
to the string type S's member S::size_type, when the search fails. Before the change,
S::npos (equal to S::size_type(-1)) was returned on failure.
std::size_t isn't the widest unsigned integer type (e.g. on usual 32-bit
platforms), the return value can change. Because there can be an allocator with a wider size_type,
and when the basic_string type S uses such an allocator, S::size_type is specified to be that
type, which in turn makes S::size_type(std::size_t(-1)) not equal to S::size_type(-1).
Do we want to restore the old return values?
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 27.4.3.8.2 [string.find] as indicated:
template<class T> constexpr size_type find(const T& t, size_type pos = 0) const noexcept(see below); […] template<class T> constexpr size_type find_last_not_of(const T& t, size_type pos = npos) const noexcept(see below);-2- Constraints: […]
-3- Effects: LetGbe the name of the function. Equivalent to:basic_string_view<charT, traits> s = *this, sv = t;return s.G(sv, pos);if (auto result = s.G(sv, pos); result == size_t(-1)) return npos; else return result;
[2025-06-10, reflector discussion]
During reflector discussion of this issue there was a preference to adjust the
proposed wording to use s.npos instead of size_t(-1).
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
[2025-12-04; Reflector poll.]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 27.4.3.8.2 [string.find] as indicated:
template<class T> constexpr size_type find(const T& t, size_type pos = 0) const noexcept(see below); […] template<class T> constexpr size_type find_last_not_of(const T& t, size_type pos = npos) const noexcept(see below);-2- Constraints: […]
-3- Effects: LetGbe the name of the function. Equivalent to:basic_string_view<charT, traits> s = *this, sv = t;return s.G(sv, pos);if (auto result = s.G(sv, pos); result == s.npos) return npos; else return result;
unique_ptr<void>::operator* is not SFINAE-friendlySection: 20.3.1.3.5 [unique.ptr.single.observers] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2025-08-24 Last modified: 2025-12-04
Priority: 3
View other active issues in [unique.ptr.single.observers].
View all other issues in [unique.ptr.single.observers].
Discussion:
LWG 2762(i) added a conditional noexcept specification to unique_ptr::operator*
to make it consistent with shared_ptr::operator*:
constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>()));
This unexpectedly makes unique_ptr<void>::operator* no longer SFINAE-friendly,
for example:
#include <memory>
template<class T> concept dereferenceable = requires(T& t) { *t; };
static_assert( dereferenceable<int *>);
static_assert(!dereferenceable<void*>);
static_assert( dereferenceable<std::shared_ptr<int >>);
static_assert(!dereferenceable<std::shared_ptr<void>>);
static_assert( dereferenceable<std::unique_ptr<int >>);
static_assert( dereferenceable<std::unique_ptr<void>>); // hard error
Given that the standard intends for operator* of shared_ptr and unique_ptr to be
SFINAE-friendly based on 20.3.2.2.6 [util.smartptr.shared.obs], regardless of the value of
static_assert, it is reasonable to assume that there should be no hard error here.
Previous resolution [SUPERSEDED]:
This wording is relative to N5014.
Modify 20.3.1.3.5 [unique.ptr.single.observers] as indicated:
constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>())see below);-1- Mandates:
-2- Preconditions:reference_converts_from_temporary_v<add_lvalue_reference_t<T>, decltype(*declval<pointer>())>isfalse.get() != nullptristrue. -3- Returns:*get(). -?- Remarks:: The exception specification is equivalent to:!requires { *declval<pointer>(); } || requires { { *declval<pointer>() } noexcept; }
[2025-08-26; Reflector discussion]
During reflector triaging it had been pointed out that a better solution would be to constrain the
operator* directly. The proposed wording has been updated to that effect.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
[2025-12-04; Reflector poll.]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 20.3.1.3.5 [unique.ptr.single.observers] as indicated:
constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>()));-?- Constraints:
-1- Mandates:*declval<pointer>()is a well-formed expression.reference_converts_from_temporary_v<add_lvalue_reference_t<T>, decltype(*declval<pointer>())>isfalse. -2- Preconditions:get() != nullptristrue. -3- Returns:*get().
std::basic_string's data() and operator[] specificationSection: 27.4.3.6 [string.access] Status: Tentatively Ready Submitter: Peter Bindels Opened: 2025-09-16 Last modified: 2026-03-06
Priority: 4
View all other issues in [string.access].
Discussion:
From the working draft N5014, the specification for operator[] in 27.4.3.6 [string.access]
p2 says:
Returns:
*(begin() + pos)ifpos < size(). Otherwise, returns a reference to an object of typecharTwith valuecharT(), where modifying the object to any value other thancharT()leads to undefined behavior.
The specification for data() in 27.4.3.8.1 [string.accessors] p1 (and p4) says, however:
Returns: A pointer
psuch thatp + i == addressof(operator[](i))for eachiin[0, size()].
The former implies that str[str.size()] is allowed to be the address of any null terminator,
while the latter restricts it to only being the null terminator belonging to the string.
operator[] to
Returns:
*(begin() + pos)ifpos <= size(). The program shall not modify the value stored atsize()to any value other thancharT(); otherwise, the behavior is undefined.
This moves it inline with the data() specification. Given the hardened precondition that
pos <= size() this does not change behavior for any in-contract access, and we do
not define what the feature does when called with broken preconditions. I have been looking at
the latter but that will be an EWG paper instead.
[2025-10-21; Reflector poll.]
Set priority to 4 after reflector poll.
"NAD. begin() + size() is not dereferenceable and should remain that way."
"Saying "if pos <= size() is redundant given the precondition above."
"The resolution removes any guarantee that the value at str[str.size()]
is charT(). Furthermore, the premise of the issue is incorrect,
returning the address of a different null terminator not belonging to the
string would make traversing it with other string operations UB, so it
has to return a reference to a terminator that's within the same array."
"*(begin() = size()) is UB, but could use *(data() + size()) instead.
Personally I'd like *end() to be valid, but that's certainly LEWG business
requiring a paper."
Previous resolution [SUPERSEDED]:
This wording is relative to N5014.
Modify 27.4.3.6 [string.access] as indicated:
constexpr const_reference operator[](size_type pos) const; constexpr reference operator[](size_type pos);-1- Hardened preconditions:
-2- Returns:pos <= size()istrue.*(begin() + pos)ifpos <= size().Otherwise, returns a reference to an object of type-3- Throws: Nothing. -4- Complexity: Constant time. -?- Remarks The program shall not modify the value stored atcharTwith valuecharT(), where modifying the object to any value other thancharT()leads to undefined behavior.size()to any value other thancharT(); otherwise, the behavior is undefined
[2025-11-11; Jonathan provides new wording]
We say that basic_string is a contiguous container, which makes the
addressof wording in c_str() and data() redundant.
The front matter says that there's a null terminator present,
so we can move the rule about not modifying the terminator there
instead of repeating it in operator[] and c_str().
We can also permit modifying the string contents through
const_cast<char*>(str.c_str())[0].
There's no reason for that to be undefined when
const_cast<string&>(str)[0] and
const_cast<string&>(str).data()[0]
are both allowed.
The only restriction should be on changing the null terminator.
Changing any other characters through c_str() const or data() const
is no different to changing them through the non-const data(),
and does not need to cause undefined behaviour.
Previous resolution [SUPERSEDED]:
This wording is relative to N5014.
Modify 27.4.3.1 [basic.string.general] as indicated:
-3- In all cases, [
data(),data() + size()] is a valid range,data() + size()points at an object with valuecharT()(a "null terminator"), andsize() <= capacity()istrue. Non-const access to the null terminator is possible, e.g. using*(data()+size()), but the program has undefined behavior if the null terminator is modified to any value other thancharT().Modify 27.4.3.6 [string.access] as indicated:
constexpr const_reference operator[](size_type pos) const; constexpr reference operator[](size_type pos);-1- Hardened Preconditions:
pos <= size()istrue.-2- Returns:
*(data() + pos).*(begin() + pos)ifpos < size(). Otherwise, returns a reference to an object of typecharTwith valuecharT(), where modifying the object to any value other thancharT()leads to undefined behavior.-3- Throws: Nothing.
-4- Complexity: Constant time.
Modify 27.4.3.8.1 [string.accessors] as indicated:
constexpr const charT* c_str() const noexcept; constexpr const charT* data() const noexcept; constexpr charT* data() noexcept;-1- Returns:
to_address(begin()).A pointerpsuch thatp + i == addressof(operator[](i))for eachiin [0,size()].-2- Complexity: Constant time.
-3- Remarks: The program shall not modify any of the values stored in the character array; otherwise, the behavior is undefined.constexpr charT* data() noexcept;
-4- Returns: A pointerpsuch thatp + i == addressof(operator[](i))for eachiin [0,size()].
-5- Complexity: Constant time.
-6- Remarks: The program shall not modify the value stored atp + size()to any value other thancharT(); otherwise, the behavior is undefined.
[2026-02-27; Jonathan provides new wording]
[2026-03-06; Reflector poll.]
Set status to Tentatively Ready after nine votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Modify 27.4.3.1 [basic.string.general] as indicated:
-3- In all cases, [
data(),data() + size()] is a valid range,data() + size()points at an object with valuecharT()(a "null terminator"), andsize() <= capacity()istrue. The program has undefined behavior if the null terminator is modified to any value other thancharT().
Modify 27.4.3.6 [string.access] as indicated:
constexpr const_reference operator[](size_type pos) const; constexpr reference operator[](size_type pos);-1- Hardened Preconditions:
pos <= size()istrue.-2- Returns:
data()[pos].*(begin() + pos)ifpos < size(). Otherwise, returns a reference to an object of typecharTwith valuecharT(), where modifying the object to any value other thancharT()leads to undefined behavior.-3- Throws: Nothing.
-4- Complexity: Constant time.
Modify 27.4.3.8.1 [string.accessors] as indicated:
constexpr const charT* c_str() const noexcept; constexpr const charT* data() const noexcept; constexpr charT* data() noexcept;-1- Returns:
to_address(begin()).A pointerpsuch thatp + i == addressof(operator[](i))for eachiin [0,size()].-2- Complexity: Constant time.
-3- Remarks: The program shall not modify any of the values stored in the character array; otherwise, the behavior is undefined.constexpr charT* data() noexcept;
-4- Returns: A pointerpsuch thatp + i == addressof(operator[](i))for eachiin [0,size()].
-5- Complexity: Constant time.
-6- Remarks: The program shall not modify the value stored atp + size()to any value other thancharT(); otherwise, the behavior is undefined.
stable_sort, stable_partition and inplace_mergeSection: 26.4 [algorithm.syn] Status: Tentatively Ready Submitter: Braden Ganetsky Opened: 2025-11-06 Last modified: 2026-01-16
Priority: Not Prioritized
View all other issues in [algorithm.syn].
Discussion:
Addresses US 157-255This applies the resolution for US 157-255.
[2026-01-16; Reflector poll.]
Set status to Tentatively Ready after ten votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 26.4 [algorithm.syn], as indicated:
namespace ranges {
template<random_access_iterator I, sentinel_for<I> S, class Comp = ranges::less,
class Proj = identity>
requires sortable<I, Comp, Proj>
constexpr I stable_sort(I first, S last, Comp comp = {}, Proj proj = {}); // hosted
template<random_access_range R, class Comp = ranges::less, class Proj = identity>
requires sortable<iterator_t<R>, Comp, Proj>
constexpr borrowed_iterator_t<R>
stable_sort(R&& r, Comp comp = {}, Proj proj = {}); // hosted
template<execution-policy Ep, random_access_iterator I, sized_sentinel_for<I> S,
class Comp = ranges::less, class Proj = identity>
requires sortable<I, Comp, Proj>
I stable_sort(Ep&& exec, I first, S last, Comp comp = {},
Proj proj = {}); // freestanding-deletedhosted
template<execution-policy Ep, sized-random-access-range R, class Comp = ranges::less,
class Proj = identity>
requires sortable<iterator_t<R>, Comp, Proj>
borrowed_iterator_t<R>
stable_sort(Ep&& exec, R&& r, Comp comp = {}, Proj proj = {}); // freestanding-deletedhosted
}
Modify 26.4 [algorithm.syn], as indicated:
namespace ranges {
template<bidirectional_iterator I, sentinel_for<I> S, class Proj = identity,
indirect_unary_predicate<projected<I, Proj>> Pred>
requires permutable<I>
constexpr subrange<I> stable_partition(I first, S last, Pred pred, // hosted
Proj proj = {});
template<bidirectional_range R, class Proj = identity,
indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
requires permutable<iterator_t<R>>
constexpr borrowed_subrange_t<R> stable_partition(R&& r, Pred pred, // hosted
Proj proj = {});
template<execution-policy Ep, random_access_iterator I, sized_sentinel_for<I> S,
class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred>
requires permutable<I>
subrange<I>
stable_partition(Ep&& exec, I first, S last, Pred pred,
Proj proj = {}); // freestanding-deletedhosted
template<execution-policy Ep, sized-random-access-range R, class Proj = identity,
indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
requires permutable<iterator_t<R>>
borrowed_subrange_t<R>
stable_partition(Ep&& exec, R&& r, Pred pred, Proj proj = {}); // freestanding-deletedhosted
}
Modify 26.4 [algorithm.syn], as indicated:
namespace ranges {
template<bidirectional_iterator I, sentinel_for<I> S, class Comp = ranges::less,
class Proj = identity>
requires sortable<I, Comp, Proj>
constexpr I
inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {}); // hosted
template<bidirectional_range R, class Comp = ranges::less, class Proj = identity>
requires sortable<iterator_t<R>, Comp, Proj>
constexpr borrowed_iterator_t<R>
inplace_merge(R&& r, iterator_t<R> middle, Comp comp = {}, Proj proj = {}); // hosted
template<execution-policy Ep, random_access_iterator I, sized_sentinel_for<I> S,
class Comp = ranges::less, class Proj = identity>
requires sortable<I, Comp, Proj>
I inplace_merge(Ep&& exec, I first, I middle, S last, Comp comp = {},
Proj proj = {}); // freestanding-deletedhosted
template<execution-policy Ep, sized-random-access-range R, class Comp = ranges::less,
class Proj = identity>
requires sortable<iterator_t<R>, Comp, Proj>
borrowed_iterator_t<R>
inplace_merge(Ep&& exec, R&& r, iterator_t<R> middle, Comp comp = {},
Proj proj = {}); // freestanding-deletedhosted
}
variant constructorSection: 22.6.3.2 [variant.ctor] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2025-11-07 Last modified: 2025-12-04
Priority: Not Prioritized
View other active issues in [variant.ctor].
View all other issues in [variant.ctor].
Discussion:
All variant constructors except the last one have a Throws: element
saying what they're allowed to throw.
This originates from an editorial pull request, where the submitter said:
"It looks like this defect is an artifact of a change between P0088R0 and P0088R1. Note how in R0 neither one of theemplaced_type_t/emplaced_index_t(as they were then called) +initializer_listconstructors have a throws clause. In R1 only one of them gained it."
Previous resolution [SUPERSEDED]:
This wording is relative to N5014.
Modify 22.6.3.2 [variant.ctor], as indicated:
template<size_t I, class U, class... Args> constexpr explicit variant(in_place_index_t<I>, initializer_list<U> il, Args&&... args);-35- Constraints:
- (35.1) —
Iis less thansizeof...(Types)and- (35.2) —
is_constructible_v<TI, initializer_list<U>&, Args...>istrue.-36- Effects: Direct-non-list-initializes the contained value of type
TIwithil, std::forward<Args>(args)....-37- Postconditions:
index()isI.-?- Throws: Any exception thrown by calling the selected constructor of
TI.-38- Remarks: If
TI’s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.
[2025-11-11; Jonathan provides improved wording]
[2025-12-04; Reflector poll.]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 22.6.3.2 [variant.ctor], as indicated:
constexpr variant() noexcept(see below);-2- Constraints:
is_default_constructible_v<T0>istrue.-3- Effects: Constructs a
variantholding a value-initialized value of typeT0.-4- Postconditions:
valueless_by_exception()isfalseandindex()is0.-5- Throws: Any exception thrown by the value-initialization of
T0.-6- Remarks: […]
constexpr variant(const variant&);-7- Effects: If
wholds a value, initializes thevariantto hold the same alternative aswand direct-initializes the contained value withGET<j>(w), wherejisw.index(). Otherwise, initializes thevariantto not hold a value.-8- Throws: Any exception thrown by
direct-initializating anythe initialization of the contained value.Tifor all i-9- Remarks: […]
constexpr variant(variant&&) noexcept(see below);-10- Constraints:
is_move_constructible_v<Ti>istruefor all i.-11- Effects: If
wholds a value, initializes thevariantto hold the same alternative aswand direct-initializes the contained value withGET<j>(std::move(w)), wherejisw.index(). Otherwise, initializes thevariantto not hold a value.-12- Throws: Any exception thrown by
move-constructing anythe initialization of the contained value.Tifor all i-13- Remarks: […]
template<class T> constexpr variant(T&&) noexcept(see below);-14- Let
Tjbe a type that is determined as follows: build an imaginary functionFUN(Ti)for each alternative typeTifor whichTi x[] = {std::forward<T>(t)};is well-formed for some invented variablex. The overloadFUN(Tj)selected by overload resolution for the expressionFUN(std::forward<T>(t))defines the alternativeTjwhich is the type of the contained value after construction.-15- Constraints: […]
-16- Effects: Initializes
*thisto hold the alternative typeTjand direct-non-list-initializes the contained value withstd::forward<T>(t).-17- Postconditions: […]
-18- Throws: Any exception thrown by the initialization of the
selected alternativecontained value.Tj-19- Remarks: […]
template<class T, class... Args> constexpr variant(in_place_type_t<T>, Args&&... args);-20- Constraints: […]
-21- Effects: Direct-non-list-initializes the contained value of type
Twithstd::forward<Args>(args)....-22- Postconditions: […]
-23- Throws: Any exception thrown by
the selected constructor ofthe initialization of the contained value.T-24- Remarks: […]
template<class T, class U, class... Args> constexpr variant(in_place_type_t<T>, initializer_list<U> li, Args&&... args);-25- Constraints: […]
-26- Effects: Direct-non-list-initializes the contained value of type
Twithil, std::forward<Args>(args)....-27- Postconditions: […]
-28- Throws: Any exception thrown by
the selected constructor ofthe initialization of the contained value.T-29- Remarks: […]
template<size_t I, class... Args> constexpr explicit variant(in_place_index_t<I>, Args&&... args);-30- Constraints:
- (30.1) —
Iis less thansizeof...(Types)and- (30.2) —
is_constructible_v<TI, Args...>istrue.-31- Effects: Direct-non-list-initializes the contained value of type
TIwithstd::forward<Args>(args)....-32- Postconditions:
index()isI.-33- Throws: Any exception thrown by
the selected constructor ofthe initialization of the contained value.Ti-34- Remarks: If
TI’s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.template<size_t I, class U, class... Args> constexpr explicit variant(in_place_index_t<I>, initializer_list<U> il, Args&&... args);-35- Constraints:
- (35.1) —
Iis less thansizeof...(Types)and- (35.2) —
is_constructible_v<TI, initializer_list<U>&, Args...>istrue.-36- Effects: Direct-non-list-initializes the contained value of type
TIwithil, std::forward<Args>(args)....-37- Postconditions:
index()isI.-?- Throws: Any exception thrown by the initialization of the contained value.
-38- Remarks: If
TI’s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.
hive::splice can throw bad_allocSection: 23.3.9.5 [hive.operations] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2025-11-07 Last modified: 2025-12-04
Priority: Not Prioritized
View other active issues in [hive.operations].
View all other issues in [hive.operations].
Discussion:
Moving blocks from the source hive to the destination hive might require
reallocating the array of pointers to blocks, so the Throws: element
should allow this.
[2025-12-04; Reflector poll.]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 23.3.9.5 [hive.operations], as indicated:
void splice(hive& x); void splice(hive&& x);-2- Preconditions:
get_allocator() == x.get_allocator()istrue.-3- Effects: If
addressof(x) == thisistrue, the behavior is erroneous and there are no effects. Otherwise, inserts the contents ofxinto*thisandxbecomes empty. Pointers and references to the moved elements ofxnow refer to those same elements but as members of*this. Iterators referring to the moved elements continue to refer to their elements, but they now behave as iterators into*this, not intox.-4- Throws:
length_errorif any ofx's active blocks are not within the bounds ofcurrent-limits, as well as any exceptions thrown by the allocator.-5- Complexity: Linear in the sum of all element blocks in
xplus all element blocks in*this.-6- Remarks: Reserved blocks in
xare not transferred into*this. Ifaddressof(x) == thisisfalse, invalidates the past-the-end iterator for bothxand*this.
operator decltype(auto)" is ill-formedSection: 21.3.5 [const.wrap.class] Status: Tentatively Ready Submitter: Jiang An Opened: 2025-11-07 Last modified: 2025-12-04
Priority: Not Prioritized
View other active issues in [const.wrap.class].
View all other issues in [const.wrap.class].
Discussion:
Following the approval of CWG 1670 in Kona 2025, the
following declaration in class template constant_wrapper, 21.3.5 [const.wrap.class],
is ill-formed:
constexpr operator decltype(auto)() const noexcept { return value; }
[2025-12-04; Reflector poll.]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 21.3.5 [const.wrap.class], class template constant_wrapper synopsis, as indicated:
[…]
template<cw-fixed-value X, class>
struct constant_wrapper : cw-operators {
static constexpr const auto & value = X.data;
using type = constant_wrapper;
using value_type = typename decltype(X)::type;
[…]
constexpr operator decltype(autovalue)() const noexcept { return value; }
};
round_to_nearest" rounding mode is unclearSection: 17.3.4 [round.style] Status: Tentatively Ready Submitter: Jan Schultke Opened: 2025-11-13 Last modified: 2026-01-16
Priority: Not Prioritized
Discussion:
Consider the specification of round_to_nearest in 17.3.4 [round.style]:
round_to_nearestif the rounding style is to the nearest representable value
It is unclear how exact ties are rounded. For example, with this rounding, would a value
that is equidistant to zero and numeric_limits<float>::min() be rounded
towards zero or away from zero?
numeric_limits<T>::round_style:
185) Equivalent to
FLT_ROUNDS. Required by ISO/IEC 10967-1:2012.
In C23 5.2.4.2.2 [Characteristics of floating types <float.h>], it is specified
that a value of 1 for FLT_ROUNDS (which equals round_to_nearest) means
to nearest, ties to even
This is also the default ISO/IEC 60559 rounding mode, and chosen by standard libraries such as
libstdc++ for numeric_limits under that assumption. Do note that C23 no longer references
ISO/IEC 10967 in any normative wording, so presumably, matching FLT_ROUNDS values means to match
the value that exists in C23, including its meaning.
[2026-01-16; Reflector poll.]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
"IEEE 754-2019 talks about what to do in case of a tie for single digit precision types, such as FP4 (E2M1). Will raise this with WG14." [E2M1 is 1 sign bit, 2 exponent bits, 1 explicit mantissa bit]
Proposed resolution:
This wording is relative to N5014.
Modify 17.3.4 [round.style] as indicated:
-1- The rounding mode for floating-point arithmetic is characterized by the values:
- (1.1) —
round_indeterminateif the rounding style is indeterminable- (1.2) —
round_toward_zeroif the rounding style is toward zero- (1.3) —
round_to_nearestif the rounding style is to the nearest representable value; if there are two equally near such values, the one with an even least significant digit is chosen- (1.4) —
round_toward_infinityif the rounding style is toward infinity- (1.5) —
round_toward_neg_infinityif the rounding style is toward negative infinity
operator delete should be constexprSection: 17.6.3.4 [new.delete.placement] Status: Tentatively Ready Submitter: Jakub Jelinek Opened: 2025-11-18 Last modified: 2025-11-26
Priority: Not Prioritized
View other active issues in [new.delete.placement].
View all other issues in [new.delete.placement].
Discussion:
The P2747R2 paper made placement operator new constexpr. At that time constexpr exceptions weren't in C++26, so that was all that was needed. But later on when P3068R5 was voted in, the P2747R2 changes look insufficient. The problem is that when you throw from a constructor during operator new, it invokes placement operator delete. And P2747R2 didn't touch that.
This makes it impossible to handle an exception thrown by a placement new-expression during constant evaluation.
[2025-11-26; Reflector poll.]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 17.6.2 [new.syn] as indicated:
constexpr void* operator new (std::size_t size, void* ptr) noexcept; constexpr void* operator new[](std::size_t size, void* ptr) noexcept; constexpr void operator delete (void* ptr, void*) noexcept; constexpr void operator delete[](void* ptr, void*) noexcept;
Modify 17.6.3.4 [new.delete.placement] as indicated:
constexpr void* operator new(std::size_t size, void* ptr) noexcept;[…]
constexpr void* operator new[](std::size_t size, void* ptr) noexcept;[…]
constexpr void operator delete(void* ptr, void*) noexcept;-7- Effects: Intentionally performs no action.
-8- Remarks: Default function called when any part of the initialization in a placement new-expression that invokes the library's non-array placement operator new terminates by throwing an exception (7.6.2.8 [expr.new]).
constexpr void operator delete[](void* ptr, void*) noexcept;-9- Effects: Intentionally performs no action.
-10- Remarks: Default function called when any part of the initialization in a placement new-expression that invokes the library's array placement operator new terminates by throwing an exception (7.6.2.8 [expr.new]).
<stdatomic.h> should provide ATOMIC_CHAR8_T_LOCK_FREESection: 32.5.12 [stdatomic.h.syn] Status: Tentatively Ready Submitter: Jiang An Opened: 2025-11-21 Last modified: 2025-12-04
Priority: Not Prioritized
View all other issues in [stdatomic.h.syn].
Discussion:
Currently, <stdatomic.h> is specified to provide atomic_char8_t but not its
corresponding ATOMIC_CHAR8_T_LOCK_FREE macro, which is self-inconsistent. Also, given
WG14 N2653 added
ATOMIC_CHAR8_T_LOCK_FREE to C's <stdatomic.h> in C23, perhaps C++ should do
the same thing in the spirit of P3348R4.
[2025-12-04; Reflector poll.]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 32.5.12 [stdatomic.h.syn], header <stdatomic.h> synopsis, as indicated:
template<class T> using std-atomic = std::atomic<T>; // exposition only #define _Atomic(T) std-atomic<T> #define ATOMIC_BOOL_LOCK_FREE see below #define ATOMIC_CHAR_LOCK_FREE see below #define ATOMIC_CHAR8_T_LOCK_FREE see below #define ATOMIC_CHAR16_T_LOCK_FREE see below #define ATOMIC_CHAR32_T_LOCK_FREE see below #define ATOMIC_WCHAR_T_LOCK_FREE see below #define ATOMIC_SHORT_LOCK_FREE see below #define ATOMIC_INT_LOCK_FREE see below #define ATOMIC_LONG_LOCK_FREE see below #define ATOMIC_LLONG_LOCK_FREE see below #define ATOMIC_POINTER_LOCK_FREE see below […]
chrono::duration<const T, P>Section: 30.5.1 [time.duration.general] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2025-11-26 Last modified: 2026-01-16
Priority: Not Prioritized
Discussion:
Using a const type as the rep for a chrono::duration causes various problems
but there seems to be no rule preventing it.
The non-const member operators that modify a duration don't work if
the rep type is const, e.g.
duration<const int>::operator++()
is typically ill-formed (unless the implementation chooses to store
remove_cv_t<rep> as the data member).
hash<duration<const int>> uses
hash<const int> which is not enabled,
so you can't hash a duration with a const rep.
Generic code that wants to perform arithmetic with the rep type
would need to remember to consistently use remove_cv_t<rep>
to work correctly with const types.
We should just disallow const rep types.
If you want a non-modifiable duration,
use const duration<R,P>
not duration<const R, P>
[2026-01-16; Reflector poll.]
Set status to Tentatively Ready after ten votes in favour during reflector poll.
This loses support for duration<volatile T, P> but that seems OK.
Proposed resolution:
This wording is relative to N5014.
Modify 30.5.1 [time.duration.general] as indicated:
-2-
Repshall be an arithmetic type or a class emulating an arithmetic type. If a specialization ofdurationis instantiated with a cv-qualified type or a specialization ofdurationtypeas the argument for the template parameterRep, the program is ill-formed.-3- If
Periodis not a specialization ofratio, the program is ill-formed. IfPeriod::numis not positive, the program is ill-formed.-4- Members of
durationdo not throw exceptions other than those thrown by the indicated operations on their representations.
meta::reflect_constant_array and related functionsSection: 21.4.3 [meta.define.static] Status: Tentatively Ready Submitter: Tomasz Kamiński Opened: 2025-11-27 Last modified: 2026-03-06
Priority: 2
View other active issues in [meta.define.static].
View all other issues in [meta.define.static].
Discussion:
As any array type (even of structural types) is not considered an structural type, per
13.2 [temp.param] p12, any invocation of reflect_constant_array/define_static_array
with a multidimensional array or span of arrays is ill-formed due to the Mandates in
21.4.3 [meta.define.static] p8 that requires range value type to be structural.
As a consequence, constant_of currently supports only single-dimensional arrays
(reflect_constant_array strips outermost extents), while multi-dimensional arrays are
rejected.
Furthermore, define_static_object currently uses define_static_array(span(addressof(t), 1)).data(),
for array types. Since for T[N] input this creates an multidimensional T[1][N] constant parameter
object, this function does not support arrays at all. Creating a distinct template
parameter object leads to emission of (otherwise unnecessary) additional symbols, and breaks the
invariant that for all supported object types &constant_of(o) == define_static_object(o).
We should use reflect_constant_array for arrays directly.
The Throws clause of reflect_constant_array was updated to include any exception
thrown by iteration over range.
[2025-12-05; LWG telecon. Wording tweaks]
[2025-12-12; LWG telecon. Further wording tweaks]
[2026-01-16; Reflector poll.]
Set priority to 2 after reflector poll.
"The new Throws: paragraph implies that meta::exception is thrown if
a copy constructor of an array element throws, but we want to propagate the
thrown exception not catch it an throw a meta::exception.
Maybe something like:"
If evaluation ofreflect_constant(including evaluation resulting fromreflect_constant_arrayinvocations) would result in an exceptionEbeing thrown, then:
Eif an exception was thrown by evaluation of an argument (if any), andmeta::exceptionotherwise.
"I suggest:"
Throws: Any of
- an exception thrown by any operation on
ror on iterators and sentinels referring tor,- an exception thrown by the evaluation of any argument of
reflect_constantor by any evaluation ofreflect_constant_array, ormeta::exceptionif any invocation ofreflect_constantwould exit via an exception.
Previous resolution [SUPERSEDED]:
This wording is relative to N5014 amended with changes from LWG 4432(i).
Modify 21.4.3 [meta.define.static] as indicated:
template<ranges::input_range R> consteval info reflect_constant_array(R&& r);[…]-8- Let
beTUranges::range_value_t<R>andTberemove_all_extents_t<U>ei be.static_cast<T>(*iti), where iti is an iterator to the ith element ofr-9- Mandates:
- (9.1) —
Tis a structural type (13.2 [temp.param]),is_constructible_v<T, ranges::range_reference_t<R>>istrue, and- (9.2) —
Tsatisfiescopy_constructible, and- (9.3) — if
Uis not an array type, thenis_constructible_v<T, ranges::range_reference_t<R>>istrue.-10- Let
Vbe the pack of values of typeinfoof the same size asr, where the ith element isand iti is an iterator to the ith element of
- (10.1) —
reflect_constant_array(*iti)ifUis an array type,- (10.2) —
reflect_constant(static_cast<T>(*iti)otherwise,ei)r.-11- Let
Pbe
- (11.1) — If
sizeof...(V) > 0istrue, then the template parameter object (13.2 [temp.param]) of type constT[sizeof...(V)]initialized with, such that{[:V:]...}P[I]is template-argument-equivalent (13.6 [temp.type]) to the object represented byV...[I]for allIin the range [0,sizeof...(V)).- (11.2) — Otherwise, the template parameter object of type
const array<T, 0>initialized with{}.-12- Returns:
^^P.-13- Throws: Any exception thrown by operations on
ror on iterators and sentinels referring tor. Any exception thrown by the evaluation of any argument ofreflect_constant.ei, ormeta::exceptionifevaluation of anyany invocation ofreflect_constant(ei)reflect_constantorreflect_constant_arraywould exit via an exception.template<class T> consteval const remove_cvref_t<T>* define_static_object(T&& t);-15- Effects:Equivalent to:
using U = remove_cvref_t<T>; if constexpr (meta::is_class_type(^^U)) { return addressof(meta::extract<const U&>(meta::reflect_constant(std::forward<T>(t)))); } else if constexpr (meta::is_array_type(^^U)) { return addressof(meta::extract<const U&>(meta::reflect_constant_array(std::forward<T>(t)))); } else { return define_static_array(span(addressof(t), 1)).data(); }
[2026-02-27; LWG telecon. Further wording tweaks]
Rebased on top of N5032
Used alternative suggestion for Throws clause.
define_static_object now uses reflect_constant also for union types.
[2026-03-06; Reflector poll.]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Modify 21.4.3 [meta.define.static] as indicated:
template<ranges::input_range R> consteval info reflect_constant_array(R&& r);
[…]-8- Let
beTUranges::range_value_t<R>andTberemove_all_extents_t<U>ei be.static_cast<T>(*iti), where iti is an iterator to the ith element ofr-9- Mandates:
- (9.1) —
Tis a structural type (13.2 [temp.param]),is_constructible_v<T, ranges::range_reference_t<R>>istrue, and- (9.2) —
Tsatisfiescopy_constructible, and- (9.3) — if
Uis not an array type, thenis_constructible_v<T, ranges::range_reference_t<R>>istrue.-10- Let
Vbe the pack of values of typeinfoof the same size asr, where the ith element isand iti is an iterator to the ith element of
- (10.1) —
reflect_constant_array(*iti)ifUis an array type,- (10.2) —
reflect_constant(static_cast<T>(*iti)otherwise,ei)r.-11- Let
Pbe
- (11.1) — If
sizeof...(V) > 0istrue, then the template parameter object (13.2 [temp.param]) of type constT[sizeof...(V)]initialized with, such that{[:V:]...}P[I]is template-argument-equivalent (13.6 [temp.type]) to the object represented byV...[I]for allIin the range [0,sizeof...(V)).- (11.2) — Otherwise, the template parameter object of type
const array<T, 0>initialized with{}.-12- Returns:
^^P.-13- Throws:
Any exception thrown by the evaluation of any ei, orAny ofmeta::exceptionif evaluation of anyreflect_constant(ei)would exit via an exception.
- -13.1- an exception thrown by any operation on
ror on iterators and sentinels referring tor,- -13.2- an exception thrown by the evaluation of any argument of
reflect_constantor by any evaluation ofreflect_constant_array, or- -13.3-
meta::exceptionif any invocation ofreflect_constantwould exit via an exception.
template<class T> consteval const remove_cvref_t<T>* define_static_object(T&& t);
-15- Effects:Equivalent to:
using U = remove_cvref_t<T>; if constexpr (meta::is_class_type(^^U) || meta::is_union_type(^^U)) { return addressof(meta::extract<const U&>(meta::reflect_constant(std::forward<T>(t)))); } else if constexpr (meta::is_array_type(^^U)) { return addressof(meta::extract<const U&>(meta::reflect_constant_array(std::forward<T>(t)))); } else { return define_static_array(span(addressof(t), 1)).data(); }
submdspan_extents and submdspan_canonicalize_slicesSection: 23.7.3.7 [mdspan.sub] Status: Tentatively Ready Submitter: Tomasz Kamiński Opened: 2025-12-16 Last modified: 2026-01-16
Priority: Not Prioritized
Discussion:
Addresses US 152-243 and PL-008.Rename submdspan_extents to subextents and submdspan_canonicalize_slices to canonical_slices.
[2026-01-16; Reflector poll.]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Modify 23.7.3.2 [mdspan.syn] as follows:
// 23.7.3.7 [mdspan.sub], submdspan creation template<class OffsetType, class LengthType, class StrideType> struct strided_slice; template<class LayoutMapping> struct submdspan_mapping_result; struct full_extent_t { explicit full_extent_t() = default; }; inline constexpr full_extent_t full_extent{}; template<class IndexType, size_t... Extents, class... SliceSpecifiers> constexpr auto submdspan_extents(const extents<IndexType, Extents...>&, SliceSpecifiers...); // [mdspan.sub.canonical], submdspan slice canonicalization template<class IndexType, size_t... Extents, class... Slices> constexpr autosubmdspan_canonicalizecanonical_slices(const extents<IndexType, Extents...>& src, Slices... slices);
Modify [mdspan.sub.canonical] as follows:
submdspanslice canonicalizationtemplate<class IndexType, size_t... Extents, class... Slices> constexpr autosubmdspan_canonicalizecanonical_slices(const extents<IndexType, Extents...>& src, Slices... slices);
Modify 23.7.3.7.5 [mdspan.sub.extents] as follows:
23.7.3.7.5 [mdspan.sub.extents]
subfunctionmdspan_extentstemplate<class IndexType, size_t... Extents, class... SliceSpecifiers> constexpr auto submdspan_extents(const extents<IndexType, Extents...>& src, SliceSpecifiers... raw_slices);-1- Let
slicesbe the pack introduced by the following declaration:auto [...slices] =submdspan_canonicalizecanonical_slices(src, raw_slices...)
Modify [mdspan.sub.map.sliceable] as follows:
-5- Returns: An object
smrof typeSMRsuch that
- -5.1-
smr.mapping.extents() == subismdspan_extents(m.extents(), valid_slices...)true; and- -5.2- […]
Modify 23.7.3.7.6.1 [mdspan.sub.map.common] as follows:
-5- Let
sub_extbe the result ofsuband letmdspan_extents(extents(), slices...)SubExtentsbedecltype(sub_ext).
Modify 23.7.3.7.7 [mdspan.sub.sub] as follows:
-2- Let
slicesbe the pack introduced by the following declaration:auto [...slices] =submdspan_canonicalizecanonical_slices(src, raw_slices...)
Section: 22.9.2.1 [template.bitset.general], 23.3.14.1 [vector.bool.pspc] Status: Tentatively Ready Submitter: Jiang An Opened: 2025-12-16 Last modified: 2026-02-18
Priority: Not Prioritized
View all other issues in [template.bitset.general].
Discussion:
We haven't explicitly specified the return values of bitset<N>::reference::operator bool() and
vector<bool, A>::reference::operator bool(), although the intended return values can be inferred
from "a class that simulates a reference to a single bit in the sequence". Moreover, specification for
bitset<N>::reference::operator~ seems completely missing, and the comment "flip the bit" seems
misleading. Implementations consistently make the operator~ return !operator bool().
[2026-02-18; Reflector poll.]
Changed resolution to say "the bit referred to by *this" instead of
"the referred to bit".
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Modify 22.9.2.1 [template.bitset.general] as indicated:
[…][…] // bit reference class reference { public: constexpr reference(const reference& x) noexcept; constexpr ~reference(); constexpr reference& operator=(bool x) noexcept; // for b[i] = x; constexpr reference& operator=(const reference& x) noexcept; // for b[i] = b[j]; constexpr const reference& operator=(bool x) const noexcept; constexpr operator bool() const noexcept; // for x = b[i]; constexpr bool operator~() const noexcept;// flips the bitconstexpr reference& flip() noexcept; // for b[i].flip(); friend constexpr void swap(reference x, reference y) noexcept; friend constexpr void swap(reference x, bool& y) noexcept; friend constexpr void swap(bool& x, reference y) noexcept; }; […]constexpr reference& reference::operator=(bool x) noexcept; constexpr reference& reference::operator=(const reference& x) noexcept; constexpr const reference& reference::operator=(bool x) const noexcept;-6- Effects: Sets the bit referred to by
-7- Returns:*thisifbool(x)istrue, and clears it otherwise.*this.constexpr reference::operator bool() const noexcept;-?- Returns:
trueif the value of bit referred to by*thisis one,falseotherwise.constexpr bool reference::operator~() const noexcept;-?- Returns:
!bool(*this).
Modify 23.3.14.1 [vector.bool.pspc] as indicated:
[…]constexpr reference& reference::operator=(bool x) noexcept; constexpr reference& reference::operator=(const reference& x) noexcept; constexpr const reference& reference::operator=(bool x) const noexcept;-7- Effects: Sets the bit referred to by
-8- Returns:*thiswhenbool(x)istrue, and clears it otherwise.*this.constexpr reference::operator bool() const noexcept;-?- Returns:
trueif the value of the bit referred to by*thisis one,falseotherwise.
constant_wrapper wording problemsSection: 21.3.5 [const.wrap.class] Status: Tentatively Ready Submitter: Matthias Wippich Opened: 2026-01-07 Last modified: 2026-01-16
Priority: Not Prioritized
View other active issues in [const.wrap.class].
View all other issues in [const.wrap.class].
Discussion:
During resolution of LWG 4383(i) prefix and postfix increment and decrement operators were changed to
template<constexpr-param T>
constexpr auto operator++(this T) noexcept -> constant_wrapper<++Y>
{ return {}; }
template<constexpr-param T>
constexpr auto operator++(this T, int) noexcept ->
constant_wrapper<Y++> { return {}; }
template<constexpr-param T>
constexpr auto operator--(this T) noexcept -> constant_wrapper<--Y>
{ return {}; }
template<constexpr-param T>
constexpr auto operator--(this T, int) noexcept ->
constant_wrapper<Y--> { return {}; }
However, we do not actually specify what Y is. Additionally, the
assignment operator has been changed to
template<constexpr-param R>
constexpr auto operator=(R) const noexcept
-> constant_wrapper<X = R::value> { return {}; }
This is grammatically not valid C++. The assignment must be parenthesized.
[2026-01-16; Reflector poll.]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Modify 21.3.5 [const.wrap.class],
class template constant_wrapper synopsis, as indicated:
struct cw-operators { // exposition only
…
// pseudo-mutators
template<constexpr-param T>
constexpr auto operator++(this T) noexcept
-> constant_wrapper<++Y (++T::value)> { return {}; }
template<constexpr-param T>
constexpr auto operator++(this T, int) noexcept
-> constant_wrapper<Y++ (T::value++)> { return {}; }
template<constexpr-param T>
constexpr auto operator--(this T) noexcept
-> constant_wrapper<--Y (--T::value)> { return {}; }
template<constexpr-param T>
constexpr auto operator--(this T, int) noexcept
-> constant_wrapper<Y-- (T::value--)> { return {}; }
…
};
template<cw-fixed-value X, class>
struct constant_wrapper : cw-operators {
static constexpr const auto & value = X.data;
using type = constant_wrapper;
using value_type = decltype(X)::type;
template<constexpr-param R>
constexpr auto operator=(R) const noexcept
-> constant_wrapper<(X = R::value)> { return {}; }
constexpr operator decltype(auto)() const noexcept { return value; }
};
init in vector_two_norm and matrix_frob_normSection: 29.9.13.9 [linalg.algs.blas1.nrm2], 29.9.13.12 [linalg.algs.blas1.matfrobnorm] Status: Tentatively Ready Submitter: Mark Hoemmen Opened: 2026-01-28 Last modified: 2026-02-13
Priority: Not Prioritized
View all other issues in [linalg.algs.blas1.nrm2].
Discussion:
This pull request discussion
points out two issues with the current wording of vector_two_norm:
If Scalar is the complex number 1 + i, then the current wording
would make vector_two_norm return sqrt(2i + nonnegative_real_number).
This would have nonzero imaginary part.
If Scalar is the real number -1.0, then the current wording would
make vector_two_norm return sqrt(-1.0 + nonnegative_real_number).
If nonnegative_real_number is less than 1, then the result would be
imaginary.
The analogous issues would also apply to matrix_frob_norm.
Scalar to be complex.
However, the point of letting init be nonzero is to support
computing the 2-norm of a vector (or the Frobenius norm of a matrix)
in multiple steps. The value of init should thus always represent a
valid 2-norm (or Frobenius norm) result.
There are two ways to fix this:
Impose a precondition on init, that it have nonnegative real part
and zero imaginary part.
Change the Returns element to take the absolute value of init.
(The 29.9 [linalg] clause generally uses "absolute value" to mean the
magnitude of a complex number, or the absolute value of a non-complex
number.)
We offer (2) as the Proposed Fix, as it is consistent with the approach we took for fixing LWG 4136(i).
[2026-02-13; Reflector poll.]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Modify 29.9.13.9 [linalg.algs.blas1.nrm2] as indicated:
[Drafting note: As a drive-by fix the missing closing parenthesis of the
decltypeform has been added.]
template<in-vector InVec, scalar Scalar> Scalar vector_two_norm(InVec v, Scalar init); template<class ExecutionPolicy, in-vector InVec, scalar Scalar> Scalar vector_two_norm(ExecutionPolicy&& exec, InVec v, Scalar init);-1- [Note 1: These functions correspond to the BLAS function
-2- Mandates:xNRM2[17]. — end note]InVec::value_typeandScalarare either a floating-point type, or a specialization ofcomplex. Letabeabs-if-needed(declval<typename InVec::value_type>())and letinit_absbeabs-if-needed(init). Then,decltype(init_abs * init_abs + a * a)is convertible toScalar. -3- Returns: The square root of the sumof the square ofwhose terms are the following:initand the squares of the absolute values of the elements ofv.
(3.1) — the square of the absolute value of
init, and(3.2) — the squares of the absolute values of the elements of
v.[Note 2: For
-4- Remarks: Ifinitequal to zero, this is the Euclidean norm (also called 2-norm) of the vectorv. — end note]Scalarhas higher precision thanInVec::value_type, then intermediate terms in the sum useScalar's precision or greater.
Modify 29.9.13.12 [linalg.algs.blas1.matfrobnorm] as indicated:
[Drafting note: As a drive-by fix one spurious
InVechas been changed toInMat]
template<in-matrix InMat, scalar Scalar> Scalar matrix_frob_norm(InMat A, Scalar init); template<class ExecutionPolicy, in-matrix InMat, scalar Scalar> Scalar matrix_frob_norm(ExecutionPolicy&& exec, InMat A, Scalar init);-2- Mandates:
-3- Returns: The square root of the sumandInVecInMat::value_typeScalarare either a floating-point type, or a specialization ofcomplex. Letabeabs-if-needed(declval<typename InMat::value_type>())and letinit_absbeabs-if-needed(init). Then,decltype(init_abs * init_abs + a * a)is convertible toScalar.of the squares ofwhose terms are the following:initand the absolute values of the elements ofA.
(3.1) — the square of the absolute value of
init, and(3.2) — the squares of the absolute values of the elements of
A.[Note 2: For
-4- Remarks: Ifinitequal to zero, this is the Frobenius norm of the matrixA. — end note]Scalarhas higher precision thanInMat::value_type, then intermediate terms in the sum useScalar's precision or greater.
data_member_spec should throw for cv-qualified unnamed bit-fieldsSection: 21.4.16 [meta.reflection.define.aggregate] Status: Tentatively Ready Submitter: Marek Polacek Opened: 2026-02-05 Last modified: 2026-02-27
Priority: Not Prioritized
View all other issues in [meta.reflection.define.aggregate].
Discussion:
DR 2229 changed 11.4.10 [class.bit] p2 to say:
An unnamed bit-field shall not be declared with a cv-qualified type.
It follows that define_aggregate should have the same limitation, and thus in:
#include <meta>
constexpr auto d1 = std::meta::data_member_spec(^^const int, { .bit_width = 1 });
constexpr auto d2 = std::meta::data_member_spec(^^volatile int, { .bit_width = 1 });
constexpr auto d3 = std::meta::data_member_spec(^^const volatile int, { .bit_width = 1 });
all three data_member_spec should throw.
[2026-02-27; Reflector poll.]
Set status to Tentatively Ready after 8 votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Modify 21.4.16 [meta.reflection.define.aggregate] as indicated:
consteval info data_member_spec(info type, data_member_options options);-4- Returns: […]
-5- Throws:meta::exceptionunless the following conditions are met:
(5.1) —
dealias(type)represents either an object type or a reference type;(5.2) — if
options.namecontains a value, then: […](5.3) — if
options.namedoes not contain a value, thenoptions.bit_widthcontains a value;(5.4) — if
options.bit_widthcontains a valueV, then
(5.4.1) —
is_integral_type(type) || is_enum_type(type)istrue,(5.4.2) —
options.alignmentdoes not contain a value,(5.4.3) —
options.no_unique_addressisfalse,(5.4.4) —
Vis not negative, and(5.4.5) — if
Vequals0, thenoptions.namedoes not contain a value; and(5.4.?) — if
options.namedoes not contain a value, thenis_const(type) || is_volatile(type)isfalse; and(5.5) — if
options.alignmentcontains a value, it is an alignment value (6.8.3 [basic.align]) not less thanalignment_of(type).
std::format transcodes for std::wformat_stringsSection: 28.5.2.2 [format.string.std] Status: Tentatively Ready Submitter: Jan Schultke Opened: 2026-02-12 Last modified: 2026-02-27
Priority: Not Prioritized
View other active issues in [format.string.std].
View all other issues in [format.string.std].
Discussion:
The current wording in 28.5.2.2 [format.string.std] paragraph 20 suggests that
the expression format(L"{}", 0) could produce a garbage or malformed string:
[…] Formatting is done as if by calling
to_charsas specified and copying the output through the output iterator of the format context.
It is not stated that transcoding takes place, so a plausible interpretation is that the output of
to_chars is copied to the output iterator directly. If the ordinary literal encoding is, say,
EBCDIC and the wide literal encoding is, say, UTF-32, simply converting char to wchar_t is
not meaningful.
[2026-02-27; Reflector poll.]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
P3876 would address that, but we need to clarify the wording for existing standards.
Proposed resolution:
This wording is relative to N5032.
Modify 28.5.2.2 [format.string.std] as indicated:
[Drafting note: 28.5.6.5 [format.string.escaped] paragraph 2 similarly references the wide literal encoding (as the associated character encoding for
charT = wchar_t. ]
-20- The meaning of some non-string presentation types is defined in terms of a call to
[Note 7: Additional padding and adjustments are performed prior to copying the output through the output iterator as specified by the format specifiers. — end note]to_chars. In such cases, let[first, last)be a range large enough to hold theto_charsoutput and value be the formatting argument value. Formatting is done as if by callingto_charsas specified , transcoding theto_charsoutput to the wide literal encoding ifcharTiswchar_t, and copying the output through the output iterator of the format context.
constant_wrapper should assign to valueSection: 21.3.5 [const.wrap.class] Status: Tentatively Ready Submitter: Tomasz Kamiński Opened: 2026-02-17 Last modified: 2026-02-27
Priority: Not Prioritized
View other active issues in [const.wrap.class].
View all other issues in [const.wrap.class].
Discussion:
During resolution of LWG 4383(i) assignment operator of constant_wrapper,
was changed to:
template<constexpr-param R>
constexpr auto operator=(R) const noexcept -> constant_wrapper<X = R::value> { return {}; }
This is incorrect (and inconsistent with other operators), as we assign to
X that is object of specialization of exposition only cw-fixed-value,
instead of value.
[2026-02-27; Reflector poll.]
Set status to Tentatively Ready after 9 votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032 with changes from 4500(i).
Modify 21.3.5 [const.wrap.class],
class template constant_wrapper synopsis, as indicated:
template<cw-fixed-value X, class>
struct constant_wrapper : cw-operators {
static constexpr const auto & value = X.data;
using type = constant_wrapper;
using value_type = decltype(X)::type;
template<constexpr-param R>
constexpr auto operator=(R) const noexcept
-> constant_wrapper<(valueX = R::value)> { return {}; }
constexpr operator decltype(auto)() const noexcept { return value; }
};
task's final_suspend should move the resultSection: 33.13.6.5 [task.promise] Status: Tentatively Ready Submitter: Dietmar Kühl Opened: 2026-02-21 Last modified: 2026-02-27
Priority: Not Prioritized
View other active issues in [task.promise].
View all other issues in [task.promise].
Discussion:
In 33.13.6.5 [task.promise] p6.3 the *result is passed to
set_value without std::moveing:
set_value(std::move(RCVR(*this)), *result).
Once set_value is called the operation state object where
result is stored just gets destroyed. The
second argument to set_value should be
std::move(*result).
[2026-02-27; Reflector poll.]
Set status to Tentatively Ready after 6 votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Change 33.13.6.5 [task.promise] p6.3 to std::move the
*result:
auto final_suspend() noexcept;-6- Returns: An awaitable object of unspecified type (7.6.2.4 [expr.await]) whose member functions arrange for the completion of the asynchronous operation associated with
STATE(*this)by invoking:-6.1- --
set_error(std::move(RCVR(*this)), std::move(e))iferrors.index()is greater than zero andeis the value held byerrors, otherwise-6.2- --
set_value(std::move(RCVR(*this)))ifis_void<T>istrue, and otherwise-6.3- --
set_value(std::move(RCVR(*this)), std::move(*result)).
await_transform needs to use as_awaitableSection: 33.13.6.5 [task.promise] Status: Tentatively Ready Submitter: Dietmar Kühl Opened: 2026-02-21 Last modified: 2026-02-27
Priority: Not Prioritized
View other active issues in [task.promise].
View all other issues in [task.promise].
Discussion:
In the await_transform overload for change_coroutine_scheduler the return statement repeats the function name instead of using as_awaitable. The return statement needs to be fixed. The change may be editorial.
Note that P3941 recommends removing change_coroutine_scheduler and the corresponding overload of await_transform entirely.
[2026-02-27; Reflector poll.]
Set status to Tentatively Ready after 7 votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Change 33.13.6.5 [task.promise] p10 to use as_awaitable in the Effects clause:
template<class Sch> auto await_transform(change_coroutine_scheduler<Sch> sch) noexcept;-10- Effects: Equivalent to:
returnawait_transformas_awaitable(just(exchange(SCHED(*this), scheduler_type(sch.scheduler))), *this);
task needs get_completion_signatures()Section: 33.13.6.2 [task.class] Status: Tentatively Ready Submitter: Dietmar Kühl Opened: 2026-02-21 Last modified: 2026-02-27
Priority: Not Prioritized
View other active issues in [task.class].
View all other issues in [task.class].
Discussion:
The changes made by P3164 mean that it isn't sufficient
to have a completion_signatures type alias in the
task class. Instead, it needs to have a
static consteval get_completion_signatures() member
function that returns the completion signatures. Instead of defining
a type alias, the function can return the same type.
[2026-02-27; Reflector poll.]
Set status to Tentatively Ready after 6 votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Change the task synopsis in 33.13.6.2 [task.class] to not have a completion_signatures type alias, but instead have a static consteval get_completion_signatures() member function that returns the completion signatures:
namespace std::execution {
template<class T = void, class Environment = env<>>
class task {
// [task.state]
template<receiver Rcvr>
class state; // exposition only
public:
using sender_concept = sender_t;
using completion_signatures = see below;
using allocator_type = see below;
using scheduler_type = see below;
using stop_source_type = see below;
using stop_token_type = decltype(declval<stop_source_type>().get_token());
using error_types = see below;
// [task.promise]
class promise_type;
task(task&&) noexcept;
~task();
template<class Self, class... Env>
static consteval auto get_completion_signatures();
template<receiver Rcvr>
state<Rcvr> connect(Rcvr&& rcvr) &&;
private:
coroutine_handle<promise_type> handle; // exposition only
};
}
Remove 33.13.6.2 [task.class] paragraph 4 (the specification moves to 33.13.6.3 [task.members]):
-4- The type aliascompletion_signaturesis a specialization ofexecution::completion_signatureswith the template arguments (in unspecified order):
-4.1- --set_value_t()ifTisvoid, andset_value_t(T)otherwise;
-4.2- -- template arguments of the specialization ofexecution::completion_signaturesdenoted byerror_types; and
-4.3- --set_stopped_t().
Add a new paragraph to 33.13.6.3 [task.members] before the specification of the connect member function, with the specification of the new get_completion_signatures member function:
template<class Self, class... Env> static consteval auto get_completion_signatures();-?- Let the type
Cbe a specialization ofexecution::completion_signatureswith the template arguments (in unspecified order):-?.1- --
set_value_t()ifTisvoid, andset_value_t(T)otherwise;-?.2- -- template arguments of the specialization of
execution::completion_signaturesdenoted byerror_types; and-?.3- --
set_stopped_t().-?- Returns:
C().
task::promise_type::await_transform declaration and definition mismatchSection: 33.13.6.5 [task.promise] Status: Tentatively Ready Submitter: Dietmar Kühl Opened: 2026-02-21 Last modified: 2026-02-27
Priority: Not Prioritized
View other active issues in [task.promise].
View all other issues in [task.promise].
Discussion:
The declarations of await_transform mismatch between the
synopsis in 33.13.6.5 [task.promise] and the definition in
33.13.6.5 [task.promise] paragraph 9:
template<class A>
auto await_transform(A&& a);
template<class Sch>
auto await_transform(change_coroutine_scheduler<Sch> sch);
vs.
template<sender Sender>
auto await_transform(Sender&& sndr) noexcept;
template<class Sch>
auto await_transform(change_coroutine_scheduler<Sch> sch) noexcept;
The declaration should be fixed to match the definition.
[2026-02-27; Reflector poll.]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5032.
Change the declarations of await_transform in the synopsis of promise_type in 33.13.6.5 [task.promise] to match the definition in 33.13.6.5 [task.promise] p9 except for omitting the noexcept specifier:
namespace std::execution {
template<class T, class Environment>
class task<T, Environment>::promise_type {
public:
...
template<class E>
unspecified yield_value(with_error<E> error);
template<class Asender Sender>
auto await_transform(A&& aSender&& sndr);
template<class Sch>
auto await_transform(change_coroutine_scheduler<Sch> sch);
unspecified get_env() const noexcept;
...
};
}
Remove the noexcept specifier from the declarations of await_transform in the definition of promise_type in 33.13.6.5 [task.promise] p9:
template<sender Sender> auto await_transform(Sender&& sndr)noexcept;-9- Returns: If
same_as<inline_scheduler, scheduler_type>istruereturnsas_awaitable(std::forward<Sender>(sndr), *this);otherwise returnsas_awaitable(affine_on(std::forward<Sender>(sndr), SCHED(*this)), *this).template<class Sch> auto await_transform(change_coroutine_scheduler<Sch> sch)-10- Effects: Equivalent to:noexcept;return await_transform(just(exchange(SCHED(*this), scheduler_type(sch.scheduler))), *this);