Revised 2026-02-04 at 12:39:41 UTC
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].
View all issues with Tentatively Ready status.
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].
View all issues with Tentatively Ready status.
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::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].
View all issues with Tentatively Ready status.
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].
View all issues with Tentatively Ready status.
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().
views::indices is underconstrainedSection: 25.6.4.1 [range.iota.overview] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2025-10-15 Last modified: 2026-01-16
Priority: Not Prioritized
View all other issues in [range.iota.overview].
View all issues with Tentatively NAD status.
Discussion:
Whether an integer-class type satisfies weakly_incrementable is unspecified according to
25.6.4.2 [range.iota.view]. For example, the library may provide a member
type alias difference_type for the integer-class type to make it weakly_incrementable, or not.
views::iota(integer-class-type(0)) is always ill-formed because
iota_view<W, Bound> requires W to be weakly_incrementable.
However, unlike views::iota, views::indices unconditionally accepts integer-class types,
which will lead to a hard error in the function body if the integer-class type is not
weakly_incrementable, which is true for both libstdc++ and MSVC-STL.
[2026-01-16; Reflector poll. Status → Tentatively NAD.]
"This is implied by the expression-equivalence."
Proposed resolution:
This wording is relative to N5014.
Modify 25.6.4.1 [range.iota.overview] as indicated:
-4- The name
views::indicesdenotes a customization point object (16.3.3.3.5 [customization.point.object]). Given subexpressionE, letTberemove_cvref_t<decltype((E))>.views::indices(E)is expression-equivalent toviews::iota(T(0), E)ifis-integer-like<T>istrueandTmodelsweakly_incrementable, and ill-formed otherwise.
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].
View all issues with Tentatively Ready status.
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].
View all issues with Tentatively Ready status.
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].
View all issues with Tentatively Ready status.
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].
View all issues with Tentatively Ready status.
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
View all issues with Tentatively Ready status.
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
Section: 32.6.4.2.1 [thread.mutex.requirements.mutex.general] Status: Tentatively NAD Submitter: jim x Opened: 2025-11-14 Last modified: 2026-01-16
Priority: Not Prioritized
View all issues with Tentatively NAD status.
Discussion:
32.6.4.2.1 [thread.mutex.requirements.mutex.general] p4 says:
For purposes of determining the existence of a data race, these behave as atomic operations (6.10.2 [intro.multithread]). The lock and unlock operations on a single mutex appears to occur in a single total order.
Even for atomic operations, the precondition for ordering them in a single total order is that
they must be memory_order::seq_cst operations, such that we can form the total order to reason.
unlock and lock. Is this a possible
total order if lock reads unlock_1, but there is a unlock_2 between them
unlock_1<unlock_2<lock
First, although we have said that lock and unlock operations behave as atomic operations,
and lock reads unlock_1, meaning that unlock_1 is coherence-ordered before lock,
however, we don't specify that they are memory_order::seq_cst operations, so
32.5.4 [atomics.order] p4 doesn't apply here
Second, for every pair of atomic operations
AandBon an objectM, whereAis coherence-ordered beforeB, the following four conditions are required to be satisfied byS:
- (4.1) — if
AandBare bothmemory_order::seq_cstoperations, thenAprecedesBinS; and
So, it is not helpful to decide that unlock_1 precedes lock in a single total order.
Similarly, excluding unlock_1 < unlock_2 < lock is not possible.
memory_order::seq_cst operations
[2026-01-16; Reflector poll. Status → Tentatively NAD.]
For atomic objects, the modification order is already a single total order, seq_cst or not.
This isn't a useful change.
Proposed resolution:
This wording is relative to N5014.
Modify 32.6.4.2.1 [thread.mutex.requirements.mutex.general] as indicated:
-4- The implementation provides lock and unlock operations, as described below. For purposes of determining the existence of a data race, these behave as atomic operations (6.10.2 [intro.multithread]). The lock and unlock operations on a single mutex appears to occur in a single total order; for this purpose, these operations are considered as
memory_order::seq_cstoperations.
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].
View all issues with Tentatively Ready status.
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].
View all issues with Tentatively Ready status.
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
View all issues with Tentatively Ready status.
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.
task::stop_token_typeSection: 33.13.6.4 [task.state] Status: Tentatively Ready Submitter: Tomasz Kamiński Opened: 2025-12-08 Last modified: 2026-01-16
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
Addresses US 249-379
It is not clear what bullet 33.13.6.4 [task.state] p4.6 is – it reads like a requirement on stop_token_type,
but if so, this paragraph is a poor place for it.
[2025-12-05 Tomasz comments]
The paragraph expresses requirements on user supplied Environment::stop_source_type,
that needs to model stoppable-source, which includes stoppable-callback-for
on associated token. We should also require that Environment::scheduler_type shall satisfy scheduler.
We also need to clarify that stop_possible and stop_requested on prom.token returns
same value as st during lifetime of asynchronous operation.
[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 33.13.6 [exec.task] as indicated:
-5-
allocator_typeshall meet the Cpp17Allocator requirements,scheduler_typeshall modelscheduler, andstop_source_typeshall modelstoppable-source.
Modify 33.13.6.4 [task.state] as indicated:
void start() & noexcept;
-4- Effects: Effects: Let
prombe the objecthandle.promise(). AssociatesSTATE(prom),RCVR(prom), andSCHED(prom)with*thisas follows:Let
- -4.1- […]
- -4.2- […]
- -4.3- […]
stbeget_stop_token(get_env(rcvr)). Initializesprom.tokenandprom.sourcesuch that during the lifetime of the asynchronous operation (33.3 [exec.async.ops]) associated with*this
- -4.4-
prom.token.stop_requested()returnsst.stop_requested();- -4.5-
prom.token.stop_possible()returnsst.stop_possible();.-4.6- for typesFnandInitsuch that bothinvocable<Fn>andconstructible_from<Fn, Init>are modeled,stop_token_type::callback_type<Fn>modelsstoppable-callback-for<Fn, stop_token_type, Init>.
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
View all issues with Tentatively Ready status.
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...)
consteval to std::meta::exception defaulted member functionsSection: 21.4.4 [meta.reflection.exception] Status: Tentatively NAD Submitter: Marek Polacek Opened: 2025-12-16 Last modified: 2026-01-28
Priority: Not Prioritized
View other active issues in [meta.reflection.exception].
View all other issues in [meta.reflection.exception].
View all issues with Tentatively NAD status.
Discussion:
CWG 3115 states that every function of consteval-only type shall be an
immediate function. This caused a problem for std::meta::exception::what() which was marked constexpr
but not consteval. To be able to mark it consteval, we had to tweak the rules about overriding by a consteval
virtual function (CWG 3117).
std::meta::exception such that it contains these
defaulted special member functions:
exception(const exception&) = default; exception(exception&&) = default; exception& operator=(const exception&) = default; exception& operator=(exception&&) = default;
which aren't consteval (and since they're not templates, they won't be
promoted to consteval as per P2564). I propose to make the four functions
consteval:
consteval exception(const exception&) = default; consteval exception(exception&&) = default; consteval exception& operator=(const exception&) = default; consteval exception& operator=(exception&&) = default;
[2026-01-28; Reflector poll. Status → Tentatively NAD.]
The second change in CWG 3115 fixes this.
Proposed resolution:
This wording is relative to N5032.
Modify 21.4.4 [meta.reflection.exception] as indicated:
namespace std::meta {
class exception : public std::exception {
private:
optional<string> what_; // exposition only
u8string u8what_; // exposition only
info from_; // exposition only
source_location where_; // exposition only
public:
consteval exception(u8string_view what, info from,
source_location where = source_location::current()) noexcept;
consteval exception(string_view what, info from,
source_location where = source_location::current()) noexcept;
consteval exception(const exception&) = default;
consteval exception(exception&&) = default;
consteval exception& operator=(const exception&) = default;
consteval exception& operator=(exception&&) = default;
constexpr const char* what() const noexcept override;
consteval u8string_view u8what() const noexcept;
consteval info from() const noexcept;
consteval source_location where() const noexcept;
};
}
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].
View all issues with Tentatively Ready status.
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; }
};