Revised 2025-10-17 at 14:57:41 UTC
std::make_optional
overloadsSection: 22.5.10 [optional.specalg] Status: Tentatively Ready Submitter: Jiang An Opened: 2021-10-23 Last modified: 2025-10-16
Priority: 3
View all issues with Tentatively Ready status.
Discussion:
Three std::make_optional
overloads are specified in 22.5.10 [optional.specalg].
The first one is specified by "Returns:" and the other two are specified by "Effects: Equivalent to:".
According to 16.3.2.4 [structure.specifications]/4, such uses of "Effects: Equivalent to:"
propagate the Constraints specified for constructors. As the selected constructor for the first
overload has "Constraints:" (22.5.3.2 [optional.ctor]/22), it seems that inconsistency is introduced here.
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll.
[2025-10-16; Status changed: New → Tentatively Ready.]
Reflector poll in 2024-07 with eight supporting votes.
Proposed resolution:
This wording is relative to N4901.
Modify 22.5.10 [optional.specalg] as indicated:
template<class T> constexpr optional<decay_t<T>> make_optional(T&& v);-3-
ReturnsEffects: Equivalent to:return optional<decay_t<T>>(std::forward<T>(v));
.
enumerate_view::iterator
constructor is explicitSection: 25.7.24.3 [range.enumerate.iterator] Status: Tentatively NAD Submitter: Jonathan Wakely Opened: 2023-03-23 Last modified: 2024-06-24
Priority: Not Prioritized
View other active issues in [range.enumerate.iterator].
View all other issues in [range.enumerate.iterator].
View all issues with Tentatively NAD status.
Discussion:
enumerate_view::iterator
has this constructor:
constexpr explicit iterator(iterator_t<Base> current, difference_type pos); // exposition only
In P2164R9 the detailed description of the function showed a default argument for the second parameter, which would justify it being explicit. However, that default argument was not present in the class synopsis and was removed from the detailed description when applying the paper to the draft.
[2023-06-01; Reflector poll]
Set status to Tentatively NAD after four votes in favour during reflector poll. The constructor is exposition-only, it doesn't make any difference to anything whether it's explicit or not.
Proposed resolution:
This wording is relative to N4944.
Modify the class synopsis in 25.7.24.3 [range.enumerate.iterator] as shown:
constexpr
explicititerator(iterator_t<Base> current, difference_type pos); // exposition only
Modify the detailed description in 25.7.24.3 [range.enumerate.iterator] as shown:
constexpr
explicititerator(iterator_t<Base> current, difference_type pos);-2- Effects: Initializes
current_
withstd::move(current)
andpos_
withpos
.
viewable_range
Section: 99 [ranges.refinements], 25.7.2 [range.adaptor.object] Status: Tentatively NAD Submitter: Jiang An Opened: 2023-03-27 Last modified: 2023-06-01
Priority: Not Prioritized
View all issues with Tentatively NAD status.
Discussion:
After LWG 3724(i), views::all
is well-constrained for view types,
and the constraints are stronger than viewable_range
.
The difference is that given an expression such that decltype
gives R
,
when decay_t<R>
is a view type and the implicit conversion of R
to decay_t<R>
is forbidden, views::all
rejects the expression,
but viewable_range
may accept R
.
So I think we should remove the additional constraints on views::all_t
.
While viewable_range
is probably not introducing any additional constraint within the standard library,
I think it is still useful to express the constraints on views::all
,
so it should be slightly adjusted to match views::all
.
Furthermore, viewable_range
is currently used in 25.7.2 [range.adaptor.object],
but given P2378R3 relaxed the requirements for range adaptor closure objects,
I think we should also apply similar relaxation for range adaptor objects.
This should have no impact on standard range adaptor objects.
[2023-06-01; Reflector poll]
Set status to Tentatively NAD after three votes in favour during reflector poll.
"First change is pointless. Second change is a duplicate of 3896(i).
Range adaptors return a view over their first argument, so they need to
require it's a viewable_range
."
Proposed resolution:
This wording is relative to N4944.
Change the definition of views::all_t
in 25.2 [ranges.syn] as indicated:
template<
viewable_rangeclass R> using all_t = decltype(all(declval<R>())); // freestanding
Change the definition of viewable_range
in 25.4.6 [range.refinements] as indicated:
-6- The
viewable_range
concept specifies the requirements of arange
type that can be converted to a view safely.template<class T> concept viewable_range = range<T> && ((view<remove_cvref_t<T>> &&
constructible_from<remove_cvref_t<T>, T>convertible_to<T, remove_cvref_t<T>>) || (!view<remove_cvref_t<T>> && (is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !is-initializer-list<T>))));
Change 25.7.2 [range.adaptor.object] as indicated:
-6- A range adaptor object is a customization point object (16.3.3.3.5 [customization.point.object]) that accepts a
as its first argument and returns a view.
viewable_rangerange[…]
-8- If a range adaptor object
adaptor
accepts more than one argument, then letrange
be an expression such thatdecltype((range))
models, let
viewable_rangerangeargs...
be arguments such thatadaptor(range, args...)
is a well-formed expression as specified in the rest of subclause 25.7 [range.adaptors], and letBoundArgs
be a pack that denotesdecay_t<decltype((args))>...
. The expressionadaptor(args...)
produces a range adaptor closure objectf
that is a perfect forwarding call wrapper (22.10.4 [func.require]) with the following properties: [...]
ranges::to
should prioritize the "reserve
" branchSection: 25.5.7.2 [range.utility.conv.to] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2023-07-17 Last modified: 2024-01-29
Priority: Not Prioritized
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with Tentatively NAD status.
Discussion:
When the constructed range object has no range version constructor, ranges::to
falls into a
branch designed specifically for C++17-compliant containers, which calls the legacy constructor that
accepts an iterator pair with C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)
.
#include <boost/container/vector.hpp>
#include <sstream>
#include <ranges>
int main() {
std::istringstream ints("1 2 3 4 5");
std::ranges::subrange s(std::istream_iterator<int>(ints),
std::istream_iterator<int>(),
5);
auto r = std::ranges::to<boost::container::vector>(s); // discard size info
}
Above, subrange
saves the size information of the stream, but ranges::to
only extracts
its iterator pair to create the object, so that the original size information is discarded, resulting in
unnecessary allocations.
reserve
" branch here because it is really designed for this situation.
[2023-10-30; Reflector poll]
Set status to Tentatively NAD after reflector poll. "This optimizes exotic cases at the expense of realistic cases."
Proposed resolution:
This wording is relative to N4950.
Modify 25.5.7.2 [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args> requires (!view<C>) constexpr C to(R&& r, Args&&... args);-1- Mandates:
C
is a cv-unqualified class type.-2- Returns: An object of type
C
constructed from the elements ofr
in the following manner:
(2.1) — If
C
does not satisfyinput_range
orconvertible_to<range_reference_t<R>, range_value_t<C>>
istrue
:
(2.1.1) — If
constructible_from<C, R, Args...>
istrue
:C(std::forward<R>(r), std::forward<Args>(args)...)
(2.1.2) — Otherwise, if
constructible_from<C, from_range_t, R, Args...>
istrue
:C(from_range, std::forward<R>(r), std::forward<Args>(args)...)
(2.1.3) — Otherwise, if
(2.1.3.1) —common_range<R>
istrue
,
(2.1.3.2) — the qualified-iditerator_traits<iterator_t<R>>::iterator_category
is valid and denotes a type that modelsderived_from<input_iterator_tag>
, and
(2.1.3.3) —constructible_from<C, iterator_t<R>, sentinel_t<R>, Args...>
istrue
:C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)(2.1.4) — Otherwise, if
(2.1.4.1) —
constructible_from<C, Args...>
istrue
, and(2.1.4.2) —
container-insertable<C, range_reference_t<R>>
istrue
:C c(std::forward<Args>(args)...); if constexpr (sized_range<R> && reservable-container<C>) c.reserve(static_cast<range_size_t<C>>(ranges::size(r))); ranges::copy(r, container-inserter<range_reference_t<R>>(c));(?.?.?) — Otherwise, if
(?.?.?.?) —
common_range<R>
istrue
,(?.?.?.?) — the qualified-id
iterator_traits<iterator_t<R>>::iterator_category
is valid and denotes a type that modelsderived_from<input_iterator_tag>
, and(?.?.?.?) —
constructible_from<C, iterator_t<R>, sentinel_t<R>, Args...>
istrue
:C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)(2.2) — Otherwise, if
input_range<range_reference_t<R>>
istrue
:to<C>(r | views::transform([](auto&& elem) { return to<range_value_t<C>>(std::forward<decltype(elem)>(elem)); }), std::forward<Args>(args)...);(2.3) — Otherwise, the program is ill-formed.
Section: 32.5.4 [atomics.order] Status: Tentatively NAD Submitter: jim x Opened: 2023-08-22 Last modified: 2023-11-03
Priority: Not Prioritized
View other active issues in [atomics.order].
View all other issues in [atomics.order].
View all issues with Tentatively NAD status.
Discussion:
Such two questions are sourced from StackOverflow:
Can the read operations in compare_exchange_strong
in different two thread read the same value?
For purposes of ordering, is atomic read-modify-write one operation or two?
Given this example:
#include <iostream> #include <atomic> #include <thread> struct SpinLock{ std::atomic<bool> atomic_; void lock(){ bool expected = false; while (!atomic_.compare_exchange_strong(expected,true,std::memory_order_release,std::memory_order_relaxed)) { } } void unlock(){ atomic_.store(false, std::memory_order_release); } }; int main(){ SpinLock spin{false}; auto t1 = std::thread([&](){ spin.lock(); spin.unlock(); }); auto t2 = std::thread([&](){ spin.lock(); spin.unlock(); }); t1.join(); t2.join(); }
In the current draft, the relevant phrasing that can interpret that only one read-modify-write operation reads the initial value false is 32.5.4 [atomics.order] p10:
Atomic read-modify-write operations shall always read the last value (in the modification order) written before the write associated with the read-modify-write operation.
However, the wording can have two meanings, each kind of read can result in different explanations for the example
The check of the violation is done before the side effect of the RMW is in the modification order, i.e. the rule is just checked at the read point.
The check of the violation is done after the side effect of the RMW is in the modification order, i.e. the rule is
checked when RMW
tries to add the side effect that is based on the read-value to the modification order, and that
side effect wouldn't be added to the modification order if the rule was violated.
With the first interpretation, the two RMW operations can read the same initial value because that value is indeed the last value in the modification order before such two RMW operations produce the side effect to the modification order.
With the second interpretation, there is only one RMW operation that can read the initial value because the latter one in the modification order would violate the rule if it read the initial value. Such two interpretations arise from that the wording doesn't clearly specify when that check is performed. So, my proposed wording is:Atomic read-modify-write operations shall always read the value from a side effect
X
, whereX
immediately precedes the side effect of the read-modify-write operation in the modification order.
This wording keeps a similar utterance to 6.10.2.2 [intro.races], and it can clearly convey the meaning
that we say the value read by RWM
is associated with the side effect of RMW
in the modification order.
[2023-11-03; Reflector poll]
NAD. The first reading isn't plausible.
Proposed resolution:
This wording is relative to N4958.
Modify 32.5.4 [atomics.order] as indicated:
-10- Atomic read-modify-write operations shall always read the
-11- Implementations should make atomic stores visible to atomic loads within a reasonable amount of time.lastvalue from a side effect X, where X immediately precedes the side effect of the read-modify-write operation(in the modification order) written before the write associated with the read-modify-write operation.
Section: 25.7.2 [range.adaptor.object] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2023-08-22 Last modified: 2024-06-24
Priority: Not Prioritized
View other active issues in [range.adaptor.object].
View all other issues in [range.adaptor.object].
View all issues with Tentatively NAD status.
Discussion:
In order to provide pipe support for user-defined range adaptors, P2387R3
removed the specification that the adaptor closure object returns a view, which conforms to the wording of ranges::to
.
void
. This makes it possible to break the previous specification when returning types that don't make sense,
for example:
#include <ranges>
struct Closure : std::ranges::range_adaptor_closure<Closure> {
struct NonCopyable {
NonCopyable(const NonCopyable&) = delete;
};
const NonCopyable& operator()(std::ranges::range auto&&);
};
auto r = std::views::iota(0) | Closure{}; // hard error in libstdc++ and MSVC-STL
Above, since the return type of the pipeline operator is declared as auto
, this causes the deleted
copy constructor to be invoked in the function body and produces a hard error.
[2023-10-30; Reflector poll]
Set status to Tentatively NAD.
"The wording says R | C
is equivalent to C(R)
,
not auto(C(R))
."
Proposed resolution:
This wording is relative to N4958.
Modify 25.7.2 [range.adaptor.object] as indicated:
-1- A range adaptor closure object is a unary function object that accepts a range argument. For a range adaptor closure object
C
and an expressionR
such thatdecltype((R))
modelsrange
, the following expressions are equivalent:[…]
-2- Given an object
t
of typeT
, where
(2.1) —
t
is a unary function object that accepts a range argument and returns a cv-unqualified class object,[…]
then the implementation ensures that
t
is a range adaptor closure object.
is-derived-from-view-interface
should require that T
is derived from view_interface<T>
Section: 25.4.5 [range.view] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2023-08-22 Last modified: 2023-10-30
Priority: Not Prioritized
View all other issues in [range.view].
View all issues with Tentatively NAD status.
Discussion:
Currently, the wording of is-derived-from-view-interface
only detects whether type T
is unambiguously
derived from one base class view_interface<U>
where U
is not required to be T
, which is not
the intention of CRTP.
[2023-10-30; Reflector poll]
Set status to Tentatively NAD.
The wording correctly handles the case where T derives from Base
which derives from view_interface<Base>
.
We don't want it to only be satisfied for direct inheritance from
view_interface<T>
, but from any specialization of
view_interface
.
Previously the concept only checked for inheritance from view_base
but it was changed when view_interface
stopped inheriting from
view_base
.
Proposed resolution:
This wording is relative to N4958.
Modify 25.4.5 [range.view] as indicated:
template<class T> constexpr bool is-derived-from-view-interface = see below; // exposition only template<class T> constexpr bool enable_view = derived_from<T, view_base> || is-derived-from-view-interface<T>;-6- For a type
T
,is-derived-from-view-interface<T>
istrue
if and only ifT
has exactly one public base classview_interface<T
U>for some typeandU
T
has no base classes of typeview_interface<U
for any other typeV>U
.V
view_interface::back
is overconstrainedSection: 25.5.3 [view.interface] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2023-10-28 Last modified: 2024-06-24
Priority: Not Prioritized
View all other issues in [view.interface].
View all issues with Tentatively NAD status.
Discussion:
Currently, view_interface
only provides the back
member when the derived class satisfies both
bidirectional_range
and common_range
, which ensures that ranges::prev
can act its sentinel.
common_range
seems to be too strict because when the derived class satisfies both
random_access_range
and sized_range
, its end iterator can still be calculated in constant time,
which is what some range adaptors currently do to greedily become common ranges.
I think we should follow similar rules to eliminate this inconsistency (demo):
#include <ranges>
constexpr auto r = std::ranges::subrange(std::views::iota(0), 5);
constexpr auto z = std::views::zip(r);
static_assert(r.back() == 4); // ill-formed
static_assert(std::get<0>(z.back()) == 4); // ok
[2023-11-07; Reflector poll]
NAD. "During the concat
discussion LEWG decided not to
support the corner case of random-access sized but not-common ranges."
"If we did want to address such ranges, would be better to enforce commonness
for random-access sized ranges by having ranges::end
return
ranges::begin(r) + ranges::size(r)
."
Proposed resolution:
This wording is relative to N4964.
Modify 25.5.3 [view.interface], class template view_interface
synopsis, as indicated:
namespace std::ranges { template<class D> requires is_class_v<D> && same_as<D, remove_cv_t<D>> class view_interface { […] public: […] constexpr decltype(auto) back() requires (bidirectional_range<D> && common_range<D>) || (random_access_range<D> && sized_range<D>); constexpr decltype(auto) back() const requires (bidirectional_range<const D> && common_range<const D>) || (random_access_range<const D> && sized_range<const D>); […] }; }
Modify 25.5.3.2 [view.interface.members] as indicated:
constexpr decltype(auto) back() requires (bidirectional_range<D> && common_range<D>) || (random_access_range<D> && sized_range<D>); constexpr decltype(auto) back() const requires (bidirectional_range<const D> && common_range<const D>) || (random_access_range<const D> && sized_range<const D>);-3- Preconditions:
-4- Effects: Equivalent to:!empty()
istrue
.auto common-arg-end = []<class R>(R& r) { if constexpr (common_range<R>) { return ranges::end(r); } else { return ranges::begin(r) + ranges::distance(r); } }; return *ranges::prev(common-arg-endranges::end(derived()));
chunk_view::outer-iterator::value_type
should provide empty
Section: 25.7.29.4 [range.chunk.outer.value] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2023-11-05 Last modified: 2024-03-11
Priority: Not Prioritized
View all other issues in [range.chunk.outer.value].
View all issues with Tentatively NAD status.
Discussion:
chunk_view::outer-iterator::value_type
can determine whether it is empty by simply checking whether the
chunk_view
's remainder_
is 0
, which makes it valuable to explicitly provide a
noexcept empty
member.
view_interface::empty
is synthesized only through the size
member when the original
sentinel and iterator type model sized_sentinel_for
, which seems overkill:
#include <cassert> #include <iostream> #include <sstream> #include <ranges> int main() { auto ints = std::istringstream{"1 2 3 4 5 6 7 8 9 10"}; for (auto chunk : std::views::istream<int>(ints) | std::views::chunk(3)) { for (auto elem : chunk) { assert(!chunk.empty()); // no matching function for call to 'empty()' std::cout << elem << " "; } assert(chunk.empty()); // ditto std::cout << "\n"; } }
[2024-03-11; Reflector poll]
Set status to Tentatively NAD after reflector poll in November 2023.
"The example shows you could use it if it existed, but not why that would be useful."
"This is a bad idea - the fact that the chunk 'shrinks' as it is iterated over is an implementation detail and not supposed to be observable."
Proposed resolution:
This wording is relative to N4964.
Modify 25.7.29.4 [range.chunk.outer.value] as indicated:
[…]namespace std::ranges { template<view V> requires input_range<V> struct chunk_view<V>::outer-iterator::value_type : view_interface<value_type> { private: chunk_view* parent_; // exposition only constexpr explicit value_type(chunk_view& parent); // exposition only public: constexpr inner-iterator begin() const noexcept; constexpr default_sentinel_t end() const noexcept; constexpr bool empty() const noexcept; constexpr auto size() const requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>; }; }constexpr default_sentinel_t end() const noexcept;-3- Returns:
default_sentinel
.constexpr bool empty() const noexcept;-?- Effects: Equivalent to:
return parent_->remainder_ == 0;
extents::index-cast
weirdnessSection: 23.7.3.3.2 [mdspan.extents.expo] Status: Tentatively Ready Submitter: Casey Carter Opened: 2023-11-29 Last modified: 2025-10-17
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
The exposition-only static member index-cast
of extents
is specified as
(23.7.3.3.2 [mdspan.extents.expo]/9):
template<class OtherIndexType> static constexpr auto index-cast(OtherIndexType&& i) noexcept;-9- Effects:
(9.1) — If
OtherIndexType
is an integral type other thanbool
, then equivalent toreturn i;
,(9.2) — otherwise, equivalent to
return static_cast<index_type>(i);
.[Note 1: This function will always return an integral type other than
bool
. Since this function's call sites are constrained on convertibility ofOtherIndexType
toindex_type
, integer-class types can use thestatic_cast
branch without loss of precision. — end note]
This function returns T
when passed an rvalue of cv-unqualified integral type T
,
but index_type
when passed a cv-qualified and/or lvalue argument of any integral type. It
would seem more consistent and easier to reason about if 9.1 was instead conditional on
remove_cvref_t<OtherIndexType>
.
[2025-10-17; Reflector poll.]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
"Doesn't matter in this case, but logically decay_t
seems like a better fit."
Proposed resolution:
This wording is relative to N4964.
Modify 23.7.3.3.2 [mdspan.extents.expo] as indicated:
template<class OtherIndexType> static constexpr auto index-cast(OtherIndexType&& i) noexcept;-9- Effects:
(9.1) — If
remove_cvref_t<OtherIndexType>
is an integral type other thanbool
, then equivalent toreturn i;
,(9.2) — otherwise, equivalent to
return static_cast<index_type>(i);
.[Note 1: This function will always return an integral type other than
bool
. Since this function's call sites are constrained on convertibility ofOtherIndexType
toindex_type
, integer-class types can use thestatic_cast
branch without loss of precision. — end note]
ranges::fold_meow
should explicitly spell out the return typeSection: 26.4 [algorithm.syn], 26.6.18 [alg.fold] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2024-05-03 Last modified: 2024-06-24
Priority: Not Prioritized
View other active issues in [algorithm.syn].
View all other issues in [algorithm.syn].
View all issues with Tentatively NAD status.
Discussion:
Unlike other algorithms, the return types of ranges::fold_meow
are specified in terms of
auto
and see below
, and its implementation details depend on the return types of
other overloads through decltype(fold_meow(...))
.
fold_right_last
)
extremely difficult even for experts, requiring several trips back and forth to different overloads
to finally understand what the actual return type is. The situation is even worse for newbies because
such a form of specifying the return type makes it impossible for the IDE to deduce the real return type,
which is extremely user-unfriendly.
I think that explicitly specifying the return type for these overloads not only greatly improves
readability but also offloads the compiler from deducing the return type, which can definitely be
considered an improvement.
The proposed resolution does not touch the Effects clause and only changes the function signature
to seek minimal changes.
[2024-06-24; Reflector poll: NAD]
Implementations are free to spell this out if desired.
Proposed resolution:
This wording is relative to N4981.
Modify 26.4 [algorithm.syn], header <algorithm>
synopsis, as indicated:
#include <initializer_list> // see 17.11.2 [initializer.list.syn] namespace std { […] namespace ranges { […] template<input_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-left-foldable<T, I> F> constexpr auto fold_left(I first, S last, T init, F f) -> decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>; template<input_range R, class T = range_value_t<R>, indirectly-binary-left-foldable<T, iterator_t<R>> F> constexpr auto fold_left(R&& r, T init, F f) -> decay_t<invoke_result_t<F&, T, range_reference_t<R>>>; template<input_iterator I, sentinel_for<I> S, indirectly-binary-left-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexpr auto fold_left_first(I first, S last, F f) -> optional<decay_t<invoke_result_t<F&, iter_value_t<I>, iter_reference_t<I>>>>; template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexpr auto fold_left_first(R&& r, F f) -> optional<decay_t<invoke_result_t<F&, range_value_t<R>, range_reference_t<R>>>>; template<bidirectional_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-right-foldable<T, I> F> constexpr auto fold_right(I first, S last, T init, F f) -> decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>; template<bidirectional_range R, class T = range_value_t<R>, indirectly-binary-right-foldable<T, iterator_t<R>> F> constexpr auto fold_right(R&& r, T init, F f) -> decay_t<invoke_result_t<F&, range_reference_t<R>, T>>; template<bidirectional_iterator I, sentinel_for<I> S, indirectly-binary-right-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexpr auto fold_right_last(I first, S last, F f) -> optional<decay_t<invoke_result_t<F&, iter_reference_t<I>, iter_value_t<I>>>>; template<bidirectional_range R, indirectly-binary-right-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexpr auto fold_right_last(R&& r, F f) -> optional<decay_t<invoke_result_t<F&, range_reference_t<R>, range_value_t<R>>>>; template<class I, class T> using fold_left_with_iter_result = in_value_result<I, T>; template<class I, class T> using fold_left_first_with_iter_result = in_value_result<I, T>; template<input_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-left-foldable<T, I> F> constexprsee belowauto fold_left_with_iter(I first, S last, T init, F f) -> fold_left_with_iter_result<I, decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>>; template<input_range R, class T = range_value_t<R>, indirectly-binary-left-foldable<T, iterator_t<R>> F> constexprsee belowauto fold_left_with_iter(R&& r, T init, F f) -> fold_left_with_iter_result<borrowed_iterator_t<R>, decay_t<invoke_result_t<F&, T, range_reference_t<R>>>>; template<input_iterator I, sentinel_for<I> S, indirectly-binary-left-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexprsee belowauto fold_left_first_with_iter(I first, S last, F f) -> fold_left_first_with_iter_result< I, optional<decay_t<invoke_result_t<F&, iter_value_t<I>, iter_reference_t<I>>>>>; template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexprsee belowauto fold_left_first_with_iter(R&& r, F f) -> fold_left_first_with_iter_result< borrowed_iterator_t<R>, optional<decay_t<invoke_result_t<F&, range_value_t<R>, range_reference_t<R>>>>>; } […] }
Modify 26.6.18 [alg.fold] as indicated:
template<input_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-left-foldable<T, I> F> constexpr auto ranges::fold_left(I first, S last, T init, F f) -> decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>; template<input_range R, class T = range_value_t<R>, indirectly-binary-left-foldable<T, iterator_t<R>> F> constexpr auto ranges::fold_left(R&& r, T init, F f) -> decay_t<invoke_result_t<F&, T, range_reference_t<R>>>;-1- Returns:
ranges::fold_left_with_iter(std::move(first), last, std::move(init), f).valuetemplate<input_iterator I, sentinel_for<I> S, indirectly-binary-left-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexpr auto ranges::fold_left_first(I first, S last, F f) -> optional<decay_t<invoke_result_t<F&, iter_value_t<I>, iter_reference_t<I>>>>; template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexpr auto ranges::fold_left_first(R&& r, F f) -> optional<decay_t<invoke_result_t<F&, range_value_t<R>, range_reference_t<R>>>>;-2- Returns:
ranges::fold_left_first_with_iter(std::move(first), last, f).valuetemplate<bidirectional_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-right-foldable<T, I> F> constexpr auto ranges::fold_right(I first, S last, T init, F f) -> decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>; template<bidirectional_range R, class T = range_value_t<R>, indirectly-binary-right-foldable<T, iterator_t<R>> F> constexpr auto ranges::fold_right(R&& r, T init, F f) -> decay_t<invoke_result_t<F&, range_reference_t<R>, T>>;-3- Effects: Equivalent to:
using U = decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>; if (first == last) return U(std::move(init)); I tail = ranges::next(first, last); U accum = invoke(f, *--tail, std::move(init)); while (first != tail) accum = invoke(f, *--tail, std::move(accum)); return accum;template<bidirectional_iterator I, sentinel_for<I> S, indirectly-binary-right-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexpr auto ranges::fold_right_last(I first, S last, F f) -> optional<decay_t<invoke_result_t<F&, iter_reference_t<I>, iter_value_t<I>>>>; template<bidirectional_range R, indirectly-binary-right-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexpr auto ranges::fold_right_last(R&& r, F f) -> optional<decay_t<invoke_result_t<F&, range_reference_t<R>, range_value_t<R>>>>;-4- Let
-5- Effects: Equivalent to:U
bedecltype(ranges::fold_right(first, last, iter_value_t<I>(*first), f))
.if (first == last) return optional<U>(); I tail = ranges::prev(ranges::next(first, std::move(last))); return optional<U>(in_place, ranges::fold_right(std::move(first), tail, iter_value_t<I>(*tail), std::move(f)));template<input_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-left-foldable<T, I> F> constexprsee belowauto ranges::fold_left_with_iter(I first, S last, T init, F f) -> fold_left_with_iter_result<I, decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>>; template<input_range R, class T = range_value_t<R>, indirectly-binary-left-foldable<T, iterator_t<R>> F> constexprsee belowauto ranges::fold_left_with_iter(R&& r, T init, F f) -> fold_left_with_iter_result<borrowed_iterator_t<R>, decay_t<invoke_result_t<F&, T, range_reference_t<R>>>>;-6- Let
-7- Effects: Equivalent to:U
bedecay_t<invoke_result_t<F&, T, iter_reference_t<I>>>
.if (first == last) return {std::move(first), U(std::move(init))}; U accum = invoke(f, std::move(init), *first); for (++first; first != last; ++first) accum = invoke(f, std::move(accum), *first); return {std::move(first), std::move(accum)};
-8- Remarks: The return type isfold_left_with_iter_result<I, U>
for the first overload andfold_left_with_iter_result<borrowed_iterator_t<R>, U>
for the second overload.template<input_iterator I, sentinel_for<I> S, indirectly-binary-left-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexprsee belowauto ranges::fold_left_first_with_iter(I first, S last, F f) -> fold_left_first_with_iter_result< I, optional<decay_t<invoke_result_t<F&, iter_value_t<I>, iter_reference_t<I>>>>>; template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexprsee belowauto ranges::fold_left_first_with_iter(R&& r, F f) -> fold_left_first_with_iter_result< borrowed_iterator_t<R>, optional<decay_t<invoke_result_t<F&, range_value_t<R>, range_reference_t<R>>>>>;-9- Let
U
bedecltype(ranges::fold_left(std::move(first), last, iter_value_t<I>(*first), f))-10- Effects: Equivalent to:
if (first == last) return {std::move(first), optional<U>()}; optional<U> init(in_place, *first); for (++first; first != last; ++first) *init = invoke(f, std::move(*init), *first); return {std::move(first), std::move(init)};
-11- Remarks: The return type isfold_left_first_with_iter_result<I, optional<U>>
for the first overload andfold_left_first_with_iter_result<borrowed_iterator_t<R>, optional<U>>
for the second overload.
std::num_get::do_get
for bool
call the overload for long
?Section: 28.3.4.3.2.3 [facet.num.get.virtuals] Status: Tentatively NAD Submitter: Jiang An Opened: 2024-09-29 Last modified: 2025-02-07
Priority: Not Prioritized
View other active issues in [facet.num.get.virtuals].
View all other issues in [facet.num.get.virtuals].
View all issues with Tentatively NAD status.
Discussion:
28.3.4.3.2.3 [facet.num.get.virtuals]/6 currently says:
Effects: If
(str.flags()&ios_base::boolalpha) == 0
then input proceeds as it would for along
except that if a value is being stored intoval
, […]
It is unclear whether an implementation is allowed to call the overload for long
in this case.
Currently, libc++'s version calls that overload, while libstdc++ and MSVC STL's don't
(example).
[2025-02-07; Reflector poll: NAD]
I think this is just a libc++ bug.
The wording says it "proceeds as it would for long
", which is not the same as
actually making a virtual call to do_get
for long
. It can either duplicate
the code from do_get
for long
, or make a non-virtual (i.e. qualified) call
to num_get::do_get
.
Proposed resolution:
ranges::cmeow
doesn't match ranges::meow
Section: 25.3 [range.access] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2024-12-17 Last modified: 2025-02-07
Priority: Not Prioritized
View all issues with Tentatively NAD status.
Discussion:
ranges::begin/rbegin/data
can be used on non-ranges as long as the object has a
begin/rbegin/data
member, this is also true for their const versions before C++23.
possibly-const-range
to the object,
which no longer worked for non-ranges due to this function requiring input_range
,
which seems to be a breaking change (demo):
#include <ranges>
struct NotRange {
int* begin();
const int* begin() const;
int* rbegin();
const int* rbegin() const;
int* data();
const int* data() const;
};
int main() {
NotRange r;
(void) std::ranges::begin(r);
(void) std::ranges::rbegin(r);
(void) std::ranges::data(r);
// The following works in C++20, fails in C++23
(void) std::ranges::cbegin(r);
(void) std::ranges::crbegin(r);
(void) std::ranges::cdata(r);
}
[2025-02-07; Reflector poll: NAD]
"We don't need to support ranges::cbegin
on non-ranges."
"Seems to be very similar to LWG 3913(i) which LWG closed as NAD."
Proposed resolution:
atomic<void*>
should use generic class templateSection: 32.5.8.5 [atomics.types.pointer] Status: Tentatively NAD Submitter: Gonzalo Brito Opened: 2025-01-16 Last modified: 2025-02-07
Priority: Not Prioritized
View other active issues in [atomics.types.pointer].
View all other issues in [atomics.types.pointer].
View all issues with Tentatively NAD status.
Discussion:
32.5.8.5 [atomics.types.pointer] p1 states (emphasis mine):
There is a partial specialization of the
atomic
class template for pointers.
which requires atomic<void*>
to use the atomic
class template for pointers.
However, the fetch_add
/_sub
member functions add a difference_type
to a T*
which requires a pointer-to-object type (these member functions are constexpr
,
so trying to support this seems unimplementable).
atomic_ref
, the 32.5.7.5 [atomics.ref.pointer] p1 states (emphasis mine):
There are specializations of the
atomic_ref
` class template for all pointer-to-object types.
which avoids this issue and applying the same form to 32.5.8.5 [atomics.types.pointer] would make
atomic<void*>
and atomic_ref<void*>
consistent.
atomic<void*>
uses the general template. Therefore, no user
code seems to be impacted.
[2025-02-07; Reflector poll: NAD]
The fetch_OP
members have "Mandates: T
is a complete object type."
and a note explaining that this means arithmetic on void*
is ill-formed.
So implementations are expected to use the partial specialization for void*
but to reject attempts at arithmetic. They all do this correctly today.
Proposed resolution:
This wording is relative to N5001.
Modify 32.5.8.5 [atomics.types.pointer] as indicated:
-1- There is a partial specialization of the
atomic
class template forpointerspointer-to-object types. Specializations of this partial specialization are standard-layout structs. They each have a trivial destructor.
vector<bool, Allocator>
mandate that Allocator::value_type
is bool
?Section: 23.3.14.1 [vector.bool.pspc] Status: Tentatively NAD Submitter: Stephan T. Lavavej Opened: 2025-03-18 Last modified: 2025-06-13
Priority: Not Prioritized
View all other issues in [vector.bool.pspc].
View all issues with Tentatively NAD status.
Discussion:
N5008 23.3.14.1 [vector.bool.pspc]/2 says:
Unless described below, all operations have the same requirements and semantics as the primary
vector
template, except that operations dealing with thebool
value type map to bit values in the container storage andallocator_traits::construct
(20.2.9.3 [allocator.traits.members]) is not used to construct these values.
23.2.2.5 [container.alloc.reqmts]/5 says:
Mandates:
allocator_type::value_type
is the same asX::value_type
.
Is vector<bool, allocator<int>>
forbidden? There's implementation divergence:
MSVC's STL enforces the mandate, while libc++ and libstdc++ accept this code, discovered while
running libc++'s tests with MSVC's STL.
vector<bool>
should be unique among containers in accepting allocator<Anything>
, then I believe that
a normative sentence should be added to 23.3.14.1 [vector.bool.pspc]/2, specifically creating an exemption
to 23.2.2.5 [container.alloc.reqmts]/5.
[2025-06-13; Reflector poll]
Set status to Tentatively NAD. This is just a bug in some implementations (now fixed in libstdc++).
Proposed resolution:
range_formatter::format
Section: 28.5.7.2 [format.range.formatter] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2025-04-18 Last modified: 2025-06-12
Priority: Not Prioritized
View all other issues in [format.range.formatter].
View all issues with Tentatively NAD status.
Discussion:
Currently, the signature of range_formatter::format
is as follows:
template<ranges::input_range R, class FormatContext> requires formattable<ranges::range_reference_t<R>, charT> && same_as<remove_cvref_t<ranges::range_reference_t<R>>, T> typename FormatContext::iterator format(R&& r, FormatContext& ctx) const;
which requires that the reference type of the range parameter must be formattable
,
and such type must be exactly T
after removing the cvref-qualifiers.
However, satisfying the latter always implies satisfying the former, as the range_formatter
class
already requires that T
must be formattable
.
There is no need to perform a redundant check here.
[2025-06-12; Reflector poll]
Set status to Tentatively NAD. This is not redundant, it might check that
const T
is formattable, which is not the same as checking that
T
is formattable.
Proposed resolution:
This wording is relative to N5008.
Modify 28.5.7.2 [format.range.formatter] as indicated:
[…]namespace std { template<class T, class charT = char> requires same_as<remove_cvref_t<T>, T> && formattable<T, charT> class range_formatter { […] template<ranges::input_range R, class FormatContext> requiresformattable<ranges::range_reference_t<R>, charT> &&same_as<remove_cvref_t<ranges::range_reference_t<R>>, T> typename FormatContext::iterator format(R&& r, FormatContext& ctx) const; }; }template<ranges::input_range R, class FormatContext> requiresformattable<ranges::range_reference_t<R>, charT> &&same_as<remove_cvref_t<ranges::range_reference_t<R>>, T> typename FormatContext::iterator format(R&& r, FormatContext& ctx) const;-11- Effects: Writes the following into
ctx.out()
, adjusted according to the range-format-spec:
basic_const_iterator
should provide iterator_type
Section: 24.5.3.3 [const.iterators.iterator] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2025-04-29 Last modified: 2025-06-13
Priority: Not Prioritized
View other active issues in [const.iterators.iterator].
View all other issues in [const.iterators.iterator].
View all issues with Tentatively Ready status.
Discussion:
Currently, iterator adaptors in <iterator>
that wrap a single iterator
such as reverse_iterator
, move_iterator
, and counted_iterator
all provide a
public iterator_type
member for users to access the underlying iterator type, except for
basic_const_iterator
(demo):
#include <iterator>
using I = int*;
using RI = std::reverse_iterator<I>;
using MI = std::move_iterator<I>;
using CI = std::counted_iterator<I>;
using BI = std::basic_const_iterator<I>;
static_assert(std::same_as<RI::iterator_type, I>);
static_assert(std::same_as<MI::iterator_type, I>);
static_assert(std::same_as<CI::iterator_type, I>);
static_assert(std::same_as<BI::iterator_type, I>); // error
It seems reasonable to add one for basic_const_iterator
for consistency.
[2025-06-12; Reflector poll]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 24.5.3.3 [const.iterators.iterator] as indicated:
namespace std { […] template<input_iterator Iterator> class basic_const_iterator { Iterator current_ = Iterator(); // exposition only using reference = iter_const_reference_t<Iterator>; // exposition only using rvalue-reference = // exposition only iter-const-rvalue-reference-t<Iterator>; public: using iterator_type = Iterator; using iterator_concept = see below; using iterator_category = see below; // not always present using value_type = iter_value_t<Iterator>; using difference_type = iter_difference_t<Iterator>; […] }; }
chrono::local_time
should be constrainedSection: 30.7.9 [time.clock.local] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2025-05-16 Last modified: 2025-08-26
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
Stream insertion for chrono::local_time
is defined in terms of conversion to
chrono::sys_time
, but not all chrono::sys_time
specializations
can be inserted into an ostream, because one of the overloads is
constrained and the other requires convertibility to chrono::sys_days
(see 30.7.2.3 [time.clock.system.nonmembers]).
This means the following code fails to compile:
#include <iostream>
#include <chrono>
template<typename T>
concept ostream_insertable = requires (std::ostream& o, const T& t) { o << t; };
using D = std::chrono::duration<double>;
int main() {
if constexpr (ostream_insertable<std::chrono::sys_time<D>>)
std::cout << std::chrono::sys_time<D>{};
if constexpr (ostream_insertable<std::chrono::local_time<D>>)
std::cout << std::chrono::local_time<D>{}; // FAIL
}
The first condition is false, because there's no overload that's suitable.
The second is true, because the operator<<
overload for
chrono::local_time
isn't constrained and so insertion appears to be valid.
But actually trying to use it is ill-formed, because it tries to convert the
local_time<D>
to a sys_time<D>
and then insert that, which isn't valid.
[2025-08-21; Reflector poll]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 30.7.9 [time.clock.local] as indicated:
template<class charT, class traits, class Duration> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const local_time<Duration>& lt);
-?- Constraints:
os << sys_time<Duration>{lt.time_since_epoch()}
is a valid expression.-2- Effects:
os << sys_time<Duration>{lt.time_since_epoch()};
-3- Returns:
os
.
layout_stride::mapping
should treat empty mappings as exhaustiveSection: 23.7.3.4.7 [mdspan.layout.stride] Status: Tentatively Ready Submitter: Tomasz Kamiński Opened: 2025-05-22 Last modified: 2025-06-13
Priority: Not Prioritized
View all other issues in [mdspan.layout.stride].
View all issues with Tentatively Ready status.
Discussion:
Mapping over an empty multidimensional index space is always exhaustive according to the corresponding definitions from 23.7.3.4.2 [mdspan.layout.reqmts] p16.
However, the current specification oflayout_stride::mapping
does not consider whether
some of the empty multidimensional index spaces are unique or exhaustive. For illustration,
the mapping with the following configuration is not considered exhaustive according to the
current specification of 23.7.3.4.7.4 [mdspan.layout.stride.obs] bullet 5.2:
extents: 2, 2, 0 strides: 2, 6, 20
This prevents the implementation from implementing sm.is_exhaustive()
as
sm.fwd-prod-of-extents(sm::extents_type::rank()) == sm.required_span_size()
.
For all mappings with size greater than zero, such an expression provides an answer consistent
with the standard. However, it always returns true
for an empty mapping, such as shown
in the example.
is_exhaustive()
to return
true
for empty mappings.
For consistency, we could update is_always_exhaustive()
to recognize mapping with
rank() == 0
, and one for which at least one of the static extents is equal to zero
(i.e., they always represent a multidimensional index space).
[2025-06-12; Reflector poll]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 23.7.3.4.7.1 [mdspan.layout.stride.overview] as indicated:
namespace std { template<class Extents> class layout_stride::mapping { […] static constexpr bool is_always_unique() noexcept { return true; } static constexpr bool is_always_exhaustive() noexcept;{ return false; }static constexpr bool is_always_strided() noexcept { return true; } […] }; }
Modify 23.7.3.4.7.4 [mdspan.layout.stride.obs] as indicated:
[…]
static constexpr bool is_always_exhaustive() noexcept;-?- Returns:
true
ifrank_
is0
or if there is a rank indexr
ofextents()
such thatextents_type::static_extent(r)
is0
, otherwisefalse
.constexpr bool is_exhaustive() const noexcept;-5- Returns:
(5.1) —
true
ifrank_
or the size of the multidimensional index spacem.extents()
is0
.(5.2) — […]
(5.3) — […]
unique_copy
passes arguments to its predicate backwardsSection: 26.7.9 [alg.unique] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2025-05-29 Last modified: 2025-10-17
Priority: Not Prioritized
View other active issues in [alg.unique].
View all other issues in [alg.unique].
View all issues with Tentatively Ready status.
Discussion:
For the unique
algorithms, 26.7.9 [alg.unique] p1 says:
1. Letpred
beequal_to{}
for the overloads with no parameterpred
, and let E be
- (1.1) —
bool(pred(*(i - 1), *i))
for the overloads in namespacestd
;- (1.2) —
bool(invoke(comp, invoke(proj, *(i - 1)), invoke(proj, *i)))
for the overloads in namespaceranges
.
However for the unique_copy
algorithms, 26.7.9 [alg.unique] p6 says
that the arguments *i
and *(i-1)
should be reversed:
6. Letpred
beequal_to{}
for the overloads with no parameterpred
, and let E be
- (6.1) —
bool(pred(*i, *(i - 1)))
for the overloads in namespacestd
;- (6.2) —
bool(invoke(comp, invoke(proj, *i), invoke(proj, *(i - 1))))
for the overloads in namespaceranges
.
This reversed order is consistent with the documentation for
SGI STL unique_copy
,
although the docs for
SGI STL unique
show reversed arguments too, and the C++ standard doesn't match that.
A survey of known implementations shows that all three of libstdc++, libc++,
and MSVC STL use the pred(*(i - 1), *i)
order for all of std::unique
,
std::unique_copy
, ranges::unique
, and ranges::unique_copy
. The range-v3
library did the same, and even the SGI STL did too (despite what its docs said).
Only two implementations were found which match the spec and use a different
argument order for unique
and unique_copy
, Casey Carter's (cmcstl2) and
Fraser Gordon's.
In the absence of any known rationale for unique
and unique_copy
to differ,
it seems sensible to make unique_copy
more consistent with unique
(and with the majority of implementations stretching back three decades).
[2025-10-17; Reflector poll.]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Fixed misplaced )
in the (6.1) change as pointed out on reflector,
and rebased on N5014.
"I remain inconvinced that this actually matters given the equivalence relation requirement."
Proposed resolution:
This wording is relative to N5014.
Modify 26.7.9 [alg.unique] as indicated:
6. Letpred
beequal_to{}
for the overloads with no parameterpred
, and let E(i
) be
- (6.1) —
bool(pred(
for the overloads in namespace*i,*(i - 1), *i))std
;- (6.2) —
bool(invoke(comp,
for the overloads in namespaceinvoke(proj, *i),invoke(proj, *(i - 1)), invoke(proj, *i)))ranges
.
chrono::hh_mm_ss
constructor is ill-formed for unsigned durationsSection: 30.9.2 [time.hms.members] Status: Tentatively Ready Submitter: Michael Welsh Duggan Opened: 2025-06-04 Last modified: 2025-06-13
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
In 30.9.2 [time.hms.members], paragraph 3, the current wording for the
constructor of hh_mm_ss
expresses some of its requirements in terms of
abs(d)
, which is assumed to be chrono::abs(chrono::duration)
.
chrono::abs
is not defined, however, for durations with an unsigned
representation. I believe that not being able to create hh_mm_ss
objects from unsigned durations is unintentional.
is_constructible_v<hh_mm_ss<ud>, ud>
is required
to be true by the standard for any duration, so making it actually work makes
a lot of sense.
[2025-06-13; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 30.9.2 [time.hms.members] as indicated:
constexpr explicit hh_mm_ss(Duration d);-3- Effects: Constructs an object of type
hh_mm_ss
which represents theDuration d
with precisionprecision
.
(3.1) — Initializes
is_neg
withd < Duration::zero()
. LetABS_D
represent-d
ifis_neg
istrue
andd
otherwise.(3.2) — Initializes
h
withduration_cast<chrono::hours>(
.abs(d)ABS_D)(3.3) — Initializes
m
withduration_cast<chrono::minutes>(
.abs(d)ABS_D - hours())(3.4) — Initializes
s
withduration_cast<chrono::seconds>(
.abs(d)ABS_D - hours() - minutes())(3.5) — If
treat_as_floating_point_v<precision::rep>
istrue
, initializesss
with. Otherwise, initializes
abs(d)ABS_D - hours() - minutes() - seconds()ss
withduration_cast<precision>(
.abs(d)ABS_D - hours() - minutes() - seconds())
std::dynamic_extent
should also be defined in <mdspan>
Section: 23.7.3.2 [mdspan.syn] Status: Tentatively Ready Submitter: Aiden Grossman Opened: 2025-06-06 Last modified: 2025-10-17
Priority: 3
View all other issues in [mdspan.syn].
View all issues with Tentatively Ready status.
Discussion:
std::dynamic_extent
can be used in certain circumstances in std::mdspan
,
such as with padded layouts. However, std::dynamic_extent
is currently only
defined in <span>
which necessitates including <span>
solely for the std::dynamic_extent
definition.
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 23.7.3.2 [mdspan.syn], header
<span>
synopsis, as indicated:// all freestanding namespace std { // constants inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max(); // 23.7.3.3 [mdspan.extents], class template extents template<class IndexType, size_t... Extents> class extents; […] }
[2025-06-10; Jonathan provides improved wording]
[2025-10-15; Reflector poll]
Set priority to 3 after reflector poll.
[2025-10-17; Reflector poll.]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 23.7.1 [views.general] as indicated:
The header
<span>
(23.7.2.1 [span.syn]) defines the viewspan
. The header<mdspan>
(23.7.3.2 [mdspan.syn]) defines the class templatemdspan
and other facilities for interacting with these multidimensional views.-?- In addition to being available via inclusion of the
<span>
header,dynamic_extent
is available when the header<mdspan>
is included.
front()
and back()
are not hardened for zero-length std::array
sSection: 23.3.3.5 [array.zero] Status: Tentatively Ready Submitter: Jan Schultke Opened: 2025-06-08 Last modified: 2025-08-26
Priority: Not Prioritized
View other active issues in [array.zero].
View all other issues in [array.zero].
View all issues with Tentatively Ready status.
Discussion:
The intent of P3471 "Standard library hardening" is clearly to provide hardened preconditions
for members of sequence containers, including std::array
. However, a zero-length std::array
dodges this
hardening by having undefined behavior for front()
and back()
explicitly specified in
23.3.3.5 [array.zero] paragraph 3.
front()
and back()
would be hardened as well, as specified in 23.2.4 [sequence.reqmts].
[2025-08-21; Reflector poll]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 23.3.3.5 [array.zero] as indicated:
-3- The effect of callingfront()
orback()
for a zero-sized array is undefined.
simd::partial_load
uses undefined identifier T
Section: 29.10.8.7 [simd.loadstore] Status: Tentatively Ready Submitter: Tim Song Opened: 2025-06-21 Last modified: 2025-08-26
Priority: Not Prioritized
View other active issues in [simd.loadstore].
View all other issues in [simd.loadstore].
View all issues with Tentatively Ready status.
Discussion:
The Effects: element of std::simd::partial_load
(after the latest rename) uses T
but that is not defined anywhere. It should be V::value_type
.
[2025-08-21; Reflector poll]
Set status to Tentatively Ready after nine votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 29.10.8.7 [simd.loadstore] as indicated:
template<class V = see below, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R> constexpr V partial_load(R&& r, flags<Flags...> f = {}); […] template<class V = see below, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags> constexpr V partial_load(I first, S last, const typename V::mask_type& mask, flags<Flags...> f = {});
-6- […]
-7- Mandates: […] -8- Preconditions: […] -9-Effects: Initializes theReturns: Abasic_simd
object whosei
th element is initialized withmask[i] && i < ranges::size(r) ? static_cast<T>(ranges::data(r)[i]) : T()
for alli
in the range of[0, V::size())
, whereT
isV::value_type
. -10- Remarks: The default argument for template parameterV
isbasic_simd<ranges::range_value_t<R>>
.
Section: 17.3.2 [version.syn], 20.2.2 [memory.syn] Status: Tentatively Ready Submitter: Yihe Li Opened: 2025-06-17 Last modified: 2025-10-14
Priority: Not Prioritized
View other active issues in [version.syn].
View all other issues in [version.syn].
View all issues with Tentatively Ready status.
Discussion:
P1642R11 (accepted in C++23) plus LWG 4189(i) (accepted in Hagenberg) added nearly the entire
<ranges>
header to freestanding.
However, the only feature-test macro being added to freestanding is __cpp_lib_ranges_cache_latest
, which seems weird,
since views::enumerate
is also added to freestanding following the blanket comment strategy, but its feature-test
macro remains not in freestanding. In retrospective, since all range algorithms are in freestanding via
P2976, all __cpp_lib_ranges_*
FTMs (except __cpp_lib_ranges_generate_random
since
ranges::generate_random
is not in freestanding) should probably be marked as freestanding.
is_sufficiently_aligned
: P2897R7 does indicate in 5.7.6.1 that the function should be
freestanding, but somehow the wording didn't say so. The following wording includes the function and its FTM anyway
since hopefully this is just an omission when wording the paper.
[2025-10-14; Reflector poll]
Set status to Tentatively Ready after nine votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 17.3.2 [version.syn], header <version>
synopsis, as indicated:
[…] #define __cpp_lib_aligned_accessor 202411L // freestanding, also in <mdspan> […] #define __cpp_lib_array_constexpr 201811L // freestanding, also in <iterator>, <array> […] #define __cpp_lib_clamp 201603L // freestanding, also in <algorithm> […] #define __cpp_lib_constexpr_numeric 201911L // freestanding, also in <numeric> […] #define __cpp_lib_function_ref 202306L // freestanding, also in <functional> #define __cpp_lib_gcd_lcm 201606L // freestanding, also in <numeric> […] #define __cpp_lib_integer_comparison_functions 202002L // freestanding, also in <utility> […] #define __cpp_lib_is_sufficiently_aligned 202411L // freestanding, also in <memory> […] #define __cpp_lib_ranges_contains 202207L // freestanding, also in <algorithm> #define __cpp_lib_ranges_enumerate 202302L // freestanding, also in <ranges> #define __cpp_lib_ranges_find_last 202207L // freestanding, also in <algorithm> #define __cpp_lib_ranges_fold 202207L // freestanding, also in <algorithm> […] #define __cpp_lib_ranges_iota 202202L // freestanding, also in <numeric> […] #define __cpp_lib_ranges_starts_ends_with 202106L // freestanding, also in <algorithm> […] #define __cpp_lib_robust_nonmodifying_seq_ops 201304L // freestanding, also in <algorithm> #define __cpp_lib_sample 201603L // freestanding, also in <algorithm> #define __cpp_lib_saturation_arithmetic 202311L // freestanding, also in <numeric> […]
Modify 20.2.2 [memory.syn], header <memory>
synopsis, as indicated:
[…] template<size_t Alignment, class T> bool is_sufficiently_aligned(T* ptr); // freestanding […]
explicit map(const Allocator&)
should be constexpr
Section: 23.4.3.1 [map.overview] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2025-07-10 Last modified: 2025-08-26
Priority: Not Prioritized
View other active issues in [map.overview].
View all other issues in [map.overview].
View all issues with Tentatively Ready status.
Discussion:
The intent of P3372R3 was for all container constructors to be
constexpr, but during application of the paper to the working draft
it was observed that one map
constructor was missed.
[2025-08-21; Reflector poll]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 23.4.3.1 [map.overview] as indicated:
// 23.4.3.2, construct/copy/destroy constexpr map() : map(Compare()) { } constexpr explicit map(const Compare& comp, const Allocator& = Allocator()); template<class InputIterator> constexpr map(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator()); template<container-compatible-range <value_type> R> constexpr map(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); constexpr map(const map& x); constexpr map(map&& x); constexpr explicit map(const Allocator&); constexpr map(const map&, const type_identity_t<Allocator>&); constexpr map(map&&, const type_identity_t<Allocator>&); constexpr map(initializer_list<value_type>, const Compare& = Compare(), const Allocator& = Allocator());
Section: 23.5.3.1 [unord.map.overview], 23.5.4.1 [unord.multimap.overview], 23.5.6.1 [unord.set.overview], 23.5.7.1 [unord.multiset.overview] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2025-07-10 Last modified: 2025-08-29
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
The intent of P3372R3 was for all container iterators to be constexpr iterators, but during application of the paper to the working draft it was observed that unordered containers don't say it for their local iterators.
[2025-08-29; Reflector poll]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 23.5.3.1 [unord.map.overview] as indicated:
-4- The types
iterator
,andconst_iterator
,local_iterator
, andconst_local_iterator
meet the constexpr iterator requirements (24.3.1 [iterator.requirements.general]).
Modify 23.5.4.1 [unord.multimap.overview] as indicated:
-4- The types
iterator
,andconst_iterator
,local_iterator
, andconst_local_iterator
meet the constexpr iterator requirements (24.3.1 [iterator.requirements.general]).
Modify 23.5.6.1 [unord.set.overview] as indicated:
-4- The types
iterator
,andconst_iterator
,local_iterator
, andconst_local_iterator
meet the constexpr iterator requirements (24.3.1 [iterator.requirements.general]).
Modify 23.5.7.1 [unord.multiset.overview] as indicated:
-4- The types
iterator
,andconst_iterator
,local_iterator
, andconst_local_iterator
meet the constexpr iterator requirements (24.3.1 [iterator.requirements.general]).
Section: 23.7.2.2.4 [span.sub] Status: Tentatively Ready Submitter: Yuhan Liu Opened: 2025-07-11 Last modified: 2025-08-26
Priority: Not Prioritized
View all other issues in [span.sub].
View all issues with Tentatively Ready status.
Discussion:
In section 23.7.2.2.4 [span.sub], paragraphs p12, p14, and p16 erroneously use the initializer list constructor for span instead of the intended iterator/count constructor.
Specifically, in these paragraphs, the standard states:
Effects: Equivalent to: return {data(), count};
or some variant of return {pointer, size}
. As reported in
GCC bug 120997
this results in a span that points to invalid stack memory.
This can be reproduced on GCC 15.1 for subspan, first, and last:
https://godbolt.org/z/r9nrdWscq.
A proposed fix (thanks to Jonathan Wakely) could look like this following:
return span<element_type>(data(), count);
for the affected paragraphs,
which would explicitly specify the constructor used.
[2025-07-11; Jonathan adds proposed resolution]
The meaning of those Effects: paragraphs was changed for C++26 by
P2447R6 which added the span(initializer_list)
constructor.
A simpler demo is:
The proposed resolution is to usebool a[5]{}; std::span<const bool> s(a); std::span<const bool> s2 = s.first(5); assert(s2.size() == 5); // OK in C++23, fails in C++26 assert(s2.data() == a); // OK in C++23, fails in C++26
R(data(), count)
instead of
{data(), count}
. The former always (uniformly) means the same thing,
but for the latter the meaning of list-initialization depends on the types.
The list-initialization form will choose the initializer-list constructor
when data()
and count
are both convertible to the element type.
[2025-08-21; Reflector poll]
Set status to Tentatively Ready after nine votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 23.7.2.2.4 [span.sub] as indicated:
template<size_t Count> constexpr span<element_type, Count> first() const;
-1- Mandates:
Count <= Extent
istrue
.-2- Hardened preconditions:
Count <= size()
istrue
.-3- Effects: Equivalent to:
return R(
where{data(), Count});R
is the return type.template<size_t Count> constexpr span<element_type, Count> last() const;
-4- Mandates:
Count <= Extent
istrue
.-5- Hardened preconditions:
Count <= size()
istrue
.-6- Effects: Equivalent to:
return R(
where{data() + (size() - Count), Count});R
is the return type.template<size_t Offset, size_t Count = dynamic_extent> constexpr span<element_type, see below> subspan() const;
-7- Mandates:
isOffset <= Extent && (Count == dynamic_extent || Count <= Extent - Offset)
true
.-8- Hardened preconditions:
isOffset <= size() && (Count == dynamic_extent || Count <= size() - Offset)
true
.-9- Effects: Equivalent to:
return span<ElementType, see below>( data() + Offset, Count != dynamic_extent ? Count : size() - Offset);
-10- Remarks: The second template argument of the returned
span
type is:Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : dynamic_extent)
constexpr span<element_type, dynamic_extent> first(size_type count) const;
-11- Hardened preconditions:
count <= size()
istrue
.-12- Effects: Equivalent to:
return R(
where{data(), count});R
is the return type.constexpr span<element_type, dynamic_extent> last(size_type count) const;
-13- Hardened preconditions:
count <= size()
istrue
.-14- Effects: Equivalent to:
return R(
where{data() + (size() - count), count});R
is the return type.constexpr span<element_type, dynamic_extent> subspan( size_type offset, size_type count = dynamic_extent) const;
-15- Hardened preconditions:
isoffset <= size() && (count == dynamic_extent || count <= size() - offset
true
.-16- Effects: Equivalent to:
wherereturn R(
{data() + offset, count == dynamic_extent ? size() - offset : count});R
is the return type.
bitset(const CharT*)
constructor needs to be constrainedSection: 22.9.2.2 [bitset.cons] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2025-07-12 Last modified: 2025-08-26
Priority: Not Prioritized
View all other issues in [bitset.cons].
View all issues with Tentatively Ready status.
Discussion:
This code might be ill-formed, with an error outside the immediate context that users cannot prevent:
#include <bitset> struct NonTrivial { ~NonTrivial() { } }; static_assert( ! std::is_constructible_v<std::bitset<1>, NonTrivial*> );
The problem is that the bitset(const CharT*)
constructor tries to instantiate
basic_string_view<NonTrivial>
to find its size_type
,
and that instantiation might ill-formed, e.g. if std::basic_string_view
or
std::char_traits
has a static assert enforcing the requirement for their
character type to be sufficiently char-like. 27.1 [strings.general]
defines a char-like type as "any non-array trivially copyable standard-layout
(6.9.1 [basic.types.general]) type T
where is_trivially_default_constructible_v<T>
is true
."
[2025-08-21; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 22.9.2.2 [bitset.cons] as indicated:
template<class charT> constexpr explicit bitset( const charT* str, typename basic_string_view<charT>::size_type n = basic_string_view<charT>::npos, charT zero = charT(’0’), charT one = charT(’1’));
-?- Constraints:
is_array_v<charT>
isfalse
,is_trivially_copyable_v<charT>
istrue
,is_standard_layout_v<charT>
istrue
, andis_trivially_default_constructible_v<charT>
istrue
.-8- Effects: As if by:
bitset(n == basic_string_view<charT>::npos ? basic_string_view<charT>(str) : basic_string_view<charT>(str, n), 0, n, zero, one)
optional<T&>::transform
Section: 22.5.4.7 [optional.ref.monadic] Status: Tentatively Ready Submitter: Giuseppe D'Angelo Opened: 2025-07-15 Last modified: 2025-10-16
Priority: Not Prioritized
View other active issues in [optional.ref.monadic].
View all other issues in [optional.ref.monadic].
View all issues with Tentatively Ready status.
Discussion:
In 22.5.4.7 [optional.ref.monadic] the specification of optional<T&>::transform
is missing an additional part of the Mandates: element compared to the primary template's
transform
(in 22.5.3.8 [optional.monadic] p8); that is, is missing to enforce that
the U
type is a valid contained type for optional
.
transform
to
use this definition. The fact that the same wording has not been applied to
optional<T&>::transform
as well looks like an oversight. I would
suggest to apply it.
[2025-10-16; Reflector poll]
Set status to Tentatively Ready after six votes in favour during reflector poll.
As
optional<remove_cv_t<invoke_result_t<F, T&>>>
is part of the signature (return type), we never enter the body to trigger the
Mandates, so it's already implicitly ill-formed if the result of f
is not a valid contained type. It's worth clarifying that though."
Proposed resolution:
This wording is relative to N5014.
Modify 22.5.4.7 [optional.ref.monadic] as indicated:
template<class F> constexpr optional<remove_cv_t<invoke_result_t<F, T&>>> transform(F&& f) const;-4- Let
-5- Mandates:U
beremove_cv_t<invoke_result_t<F, T&>>
.U
is a valid contained type foroptional
. The declarationU u(invoke(std::forward<F>(f), *val ));is well-formed for some invented variable
u
.
optional<T&>::emplace
Section: 22.5.4.3 [optional.ref.assign] Status: Tentatively Ready Submitter: Giuseppe D'Angelo Opened: 2025-07-15 Last modified: 2025-08-28
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
The specification for optional<T&>::emplace
in 22.5.4.3 [optional.ref.assign]
is not specifying the returned value via a Returns: element; however the
function does indeed return something (a T&
). Such a Returns: element is there
for the primary template's emplace
(cf. 22.5.3.4 [optional.assign]).
[2025-08-27; Reflector poll]
Set status to Tentatively Ready after nine votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 22.5.4.3 [optional.ref.assign] as indicated:
template<class U> constexpr T& emplace(U&& u) noexcept(is_nothrow_constructible_v<T&, U>);-4- Constraints: […]
-5- Effects: Equivalent to:convert-ref-init-val(std::forward<U>(u))
. -?- Returns:*val
.
condition_variable{_any}::wait_{for, until}
should take timeout by valueSection: 32.7.4 [thread.condition.condvar], 32.7.5 [thread.condition.condvarany] Status: Tentatively Ready Submitter: Hui Xie Opened: 2025-07-19 Last modified: 2025-08-29
Priority: Not Prioritized
View other active issues in [thread.condition.condvar].
View all other issues in [thread.condition.condvar].
View all issues with Tentatively Ready status.
Discussion:
At the moment, both condition_variable
and condition_variable_any
's
wait_for
and wait_until
take the timeout time_point
/duration
by
const
reference. This can cause surprising behaviour. Given the following
example (thanks to Tim Song):
struct Task { system_clock::time_point deadline; // stuff }; std::mutex mtx; std::condition_variable cv; std::priority_queue<Task, vector<Task>, CompareDeadlines> queue; // thread 1 std::unique_lock lck(mtx); if (queue.empty()) { cv.wait(lck); } else { cv.wait_until(lck, queue.top().deadline); } // thread 2 std::lock_guard lck(mtx); queue.push(/* some task */); cv.notify_one();
From the user's point of view, it is sufficiently locked on both threads. However,
due to the fact that the time_point
is taken by reference, and that both libc++
and libstdc++'s implementation will read the value again after waking up, this
will read a dangling reference of the time_point
.
condition_variable{_any}::wait_{for, until}
.
Basically the user claims that these functions take time_point
/duration
by const
reference, if the user modifies the time_point
/duration
on another thread with
the same mutex, they can get unexpected return value for condition_variable
, and
data race for conditional_variable_any
.
Bug report here.
Reproducer (libstdc++ has the same behaviour as ours) on godbolt.
std::mutex mutex; std::condition_variable cv; auto timeout = std::chrono::steady_clock::time_point::max(); // Thread 1: std::unique_lock lock(mutex); const auto status = cv.wait_until(lock, timeout); // Thread 2: std::unique_lock lock(mutex); cv.notify_one(); timeout = std::chrono::steady_clock::time_point::min();
So basically the problem was that when we return whether there is no_timeout
or timeout
at the end of the function, we read the const
reference again, which can be changed since
the beginning of the function. For condition_variable
, it is "unexpected results" according
to the user. And in conditional_variable_any
, we actually unlock the user lock and acquire
our internal lock, then read the input again, so this is actually a data race.
wait_for
, the spec has
Effects: Equivalent to:
return wait_until(lock, chrono::steady_clock::now() + rel_time);
So the user can claim our implementation is not conforming because the spec says there needs
to be a temporary time_point
(now + duration
) created and since it should operate on this
temporary time_point
. There shouldn't be any unexpected behaviour or data race .
wait_until
it is unclear whether the spec has implications that implementations are allowed
to read abs_time
while the user's lock is unlocked.
it is also unclear if an implementation is allowed to return timeout
if cv
indeed does
not wait longer than the original value of timeout
. If it is not allowed, implementations
will have to make a local copy of the input rel_time
or abs_time
, which defeats the purpose
of taking arguments by const
reference.
For both of the examples, Ville has a great comment in the reflector:
It seems like a whole bag of problems goes away if these functions just take the timeout by value?
libc++ implementers have strong preference just changing the API to take these arguments by value, and it is not an ABI break for us as the function signature has changed.
[2025-08-29; Reflector poll]
Set status to Tentatively Ready after nine votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 32.7.4 [thread.condition.condvar] as indicated:
[…]namespace std { class condition_variable { public: […] template<class Predicate> void wait(unique_lock<mutex>& lock, Predicate pred); template<class Clock, class Duration> cv_status wait_until(unique_lock<mutex>& lock,constchrono::time_point<Clock, Duration>&abs_time); template<class Clock, class Duration, class Predicate> bool wait_until(unique_lock<mutex>& lock,constchrono::time_point<Clock, Duration>&abs_time, Predicate pred); template<class Rep, class Period> cv_status wait_for(unique_lock<mutex>& lock,constchrono::duration<Rep, Period>&rel_time); template<class Rep, class Period, class Predicate> bool wait_for(unique_lock<mutex>& lock,constchrono::duration<Rep, Period>&rel_time, Predicate pred); […] }; }template<class Clock, class Duration> cv_status wait_until(unique_lock<mutex>& lock,constchrono::time_point<Clock, Duration>&abs_time);-17- Preconditions: […]
[…]template<class Rep, class Period> cv_status wait_for(unique_lock<mutex>& lock,constchrono::duration<Rep, Period>&rel_time);-23- Preconditions: […]
[…]template<class Clock, class Duration, class Predicate> bool wait_until(unique_lock<mutex>& lock,constchrono::time_point<Clock, Duration>&abs_time, Predicate pred);-29- Preconditions: […]
[…]template<class Rep, class Period, class Predicate> bool wait_for(unique_lock<mutex>& lock,constchrono::duration<Rep, Period>&rel_time, Predicate pred);-35- Preconditions: […]
[…]
Modify 32.7.5.1 [thread.condition.condvarany.general] as indicated:
namespace std { class condition_variable_any { public: […] // 32.7.5.2 [thread.condvarany.wait], noninterruptible waits template<class Lock> void wait(Lock& lock); template<class Lock, class Predicate> void wait(Lock& lock, Predicate pred); template<class Lock, class Clock, class Duration> cv_status wait_until(Lock& lock,constchrono::time_point<Clock, Duration>&abs_time); template<class Lock, class Clock, class Duration, class Predicate> bool wait_until(Lock& lock,constchrono::time_point<Clock, Duration>&abs_time, Predicate pred); template<class Lock, class Rep, class Period> cv_status wait_for(Lock& lock,constchrono::duration<Rep, Period>&rel_time); template<class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock,constchrono::duration<Rep, Period>&rel_time, Predicate pred); // 32.7.5.3 [thread.condvarany.intwait], interruptible waits template<class Lock, class Predicate> bool wait(Lock& lock, stop_token stoken, Predicate pred); template<class Lock, class Clock, class Duration, class Predicate> bool wait_until(Lock& lock, stop_token stoken,constchrono::time_point<Clock, Duration>&abs_time, Predicate pred); template<class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock, stop_token stoken,constchrono::duration<Rep, Period>&rel_time, Predicate pred); }; }
Modify 32.7.5.2 [thread.condvarany.wait] as indicated:
[…]template<class Lock, class Clock, class Duration> cv_status wait_until(Lock& lock,constchrono::time_point<Clock, Duration>&abs_time);-6- Effects: […]
[…]template<class Lock, class Rep, class Period> cv_status wait_for(Lock& lock,constchrono::duration<Rep, Period>&rel_time);-11- Effects: […]
[…]template<class Lock, class Clock, class Duration, class Predicate> bool wait_until(Lock& lock,constchrono::time_point<Clock, Duration>&abs_time, Predicate pred);-16- Effects: […]
[…]template<class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock,constchrono::duration<Rep, Period>&rel_time, Predicate pred);-19- Effects: […]
Modify 32.7.5.3 [thread.condvarany.intwait] as indicated:
[…]template<class Lock, class Clock, class Duration, class Predicate> bool wait_until(Lock& lock, stop_token stoken,constchrono::time_point<Clock, Duration>&abs_time, Predicate pred);-7- Effects: […]
[…]template<class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock, stop_token stoken,constchrono::duration<Rep, Period>&rel_time, Predicate pred);-13- Effects: […]
type_order
templateSection: 17.12.7 [compare.type] Status: Tentatively Ready Submitter: Daniel Krügler Opened: 2025-07-27 Last modified: 2025-10-14
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
The recently approved paper P2830R10 proposes to add the new type_order
type traits to 17.12 [cmp] (and thus outside of 21.3 [type.traits]),
which has the subtle and most likely unintended effect, that it doesn't fall under the
general requirement expressed in 21.3.2 [meta.rqmts] p4,
Unless otherwise specified, the behavior of a program that adds specializations for any of the templates specified in 21.3.2 [meta.rqmts] is undefined.
and so in principle the explicit allowance specified in 16.4.5.2.1 [namespace.std] p2,
Unless explicitly prohibited, a program may add a template specialization for any standard library class template to namespace
std
[…]
holds. So we need to add extra wording to the type_order
specification in
17.12.7 [compare.type] to prohibit such program specializations.
The most simple one would mimic the wording in 21.3.2 [meta.rqmts] p4 quoted above.
Instead of introducing just another undefined opportunity to run into undefined
behaviour it has been pointed out that we could follow the approach taken by std::initializer_list
and make the program ill-formed in this case, as specified in 17.11.2 [initializer.list.syn] p2:
If an explicit specialization or partial specialization of
initializer_list
is declared, the program is ill-formed.
I think ill-formed would be better. It shouldn't be difficult for implementations to have special cases that are disallowed.
Given the already existing experience with std::initializer_list
the proposed wording below
therefore follows the ill-formed program approach.
[2025-10-14; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 17.12.7 [compare.type] as indicated:
template<class T, class U> struct type_order;-2- The name
-?- If an explicit specialization or partial specialization oftype_order
denotes a Cpp17BinaryTypeTrait (21.3.2 [meta.rqmts]) with a base characteristic ofintegral_constant<strong_ordering, TYPE-ORDER(T, U)>
.type_order
is declared, the program is ill-formed. -3- Recommended practice: The order should be lexicographical on parameter-type-lists and template argument lists.
allocator_arg_t
/allocator_arg
in the description
of uses-allocator constructionSection: 20.2.8.2 [allocator.uses.construction] Status: Tentatively Ready Submitter: Jiang An Opened: 2025-08-06 Last modified: 2025-10-14
Priority: Not Prioritized
View other active issues in [allocator.uses.construction].
View all other issues in [allocator.uses.construction].
View all issues with Tentatively Ready status.
Discussion:
Currently, 20.2.8.2 [allocator.uses.construction] bullet 2.2 states:
Otherwise, if
T
has a constructor invocable asT(allocator_arg, alloc, args...)
(leading-allocator convention), […]
However, when forming construction arguments in the utility functions, we're actually using cv-unqualified
rvalue of allocator_arg_t
, which can be inferred from using plain allocator_arg_t
but not
const allocator_arg_t&
in 20.2.8.2 [allocator.uses.construction] bullet 5.2.
allocator_arg_t
is considered correct, I think we should fix the description.
[2025-10-14; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Unless the std::allocator_arg
tag object is not supposed to be used,
wouldn't it make more sense to preserve the
"if T
has a constructor invocable as T(allocator_arg, alloc, args...)
"
wording and change every allocator_arg_t
into
const allocator_arg_t&
, so that we check for construction
from the const tag object, and then actually use a const value in the
constructor arguments.
Strongly don't care though.
Proposed resolution:
This wording is relative to N5014.
Modify 20.2.8.2 [allocator.uses.construction] as indicated:
-2- The following utility functions support three conventions for passing
alloc
to a constructor:
(2.1) — […]
(2.2) — Otherwise, if
T
has a constructor invocable asT(
(leading-allocator convention), then uses-allocator construction chooses this constructor form.allocator_argallocator_arg_t{}, alloc, args...)(2.3) — […]
Section: 16.4.4.2 [utility.arg.requirements] Status: Tentatively Ready Submitter: Jiang An Opened: 2025-08-15 Last modified: 2025-10-14
Priority: Not Prioritized
View other active issues in [utility.arg.requirements].
View all other issues in [utility.arg.requirements].
View all issues with Tentatively Ready status.
Discussion:
The meaning of "resource" in the Cpp17Destructible requirements cannot be inferred
from the standard wording and it seems unlikely that the standard will determine its meaning
in the future. What are considered as resources generally depends on users' intent, so the
standard shouldn't determine the well-definedness of a program execution due to it. Moreover,
the wording doesn't seem to consider shared ownership, which can be represented by shared_ptr
.
[2025-10-14; Reflector poll]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 16.4.4.2 [utility.arg.requirements], Table 35 [tab:cpp17.destructible] as indicated:
Table 35 — Cpp17Destructible
requirements [tab:cpp17.destructible]Expression Post-condition u.~T()
All resources owned byNo exception is propagated.u
are reclaimed, n[Note 3: Array types and non-object types are not Cpp17Destructible. — end note]
hive::erase_if
reevaluate end()
to avoid UBSection: 23.3.9.6 [hive.erasure] Status: Tentatively Ready Submitter: Frank Birbacher Opened: 2025-08-16 Last modified: 2025-08-29
Priority: Not Prioritized
View all other issues in [hive.erasure].
View all issues with Tentatively Ready status.
Discussion:
Background: https://github.com/cplusplus/draft/pull/8162
For 23.3.9.6 [hive.erasure] p2, the defining code must not cache the end-iterator. In case the last element of the sequence is removed, the past-the-end iterator will be invalidated. This will trigger UB in the loop condition. Instead, re-evaluateend()
each time.
[2025-08-29; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
[Drafting note: There are other ways to fix this code while keeping the caching behaviour, but I don't see any particular reason to do so for the definition of the effects.]
Modify 23.3.9.6 [hive.erasure] as indicated:
template<class T, class Allocator, class Predicate> typename hive<T, Allocator>::size_type erase_if(hive<T, Allocator>& c, Predicate pred);-2- Effects: Equivalent to:
auto original_size = c.size(); for (auto i = c.begin(), last = c.end(); i !=lastc.end(); ) { if (pred(*i)) { i = c.erase(i); } else { ++i; } } return original_size - c.size();
task::connect()
Section: 33.13.6.2 [task.class] Status: Tentatively Ready Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-17
Priority: Not Prioritized
View other active issues in [task.class].
View all other issues in [task.class].
View all issues with Tentatively Ready status.
Discussion:
Coroutines can't be copied. Thus, a task
can be
connect()
just once. To represent that
task::connect()
should be rvalue reference qualified
but currently it isn't.
[2025-10-17; Reflector poll.]
Set status to Tentatively Ready after five votes in favour during reflector poll.
"It's nice to rvalue qualify such a function, but it is not strictly necessary."
Proposed resolution:
In the synopsis in 33.13.6.2 [task.class] add rvalue
reference qualification to task::connect()
:
namespace std::execution { template<class T, class Environment> class task { ... template<receiver Rcvr> state<Rcvr> connect(Rcvr&& rcvr) &&; ... } }
In the specification in 33.13.6.3 [task.members] paragraph 3 add rvalue
reference qualification to task::connect()
:
template<receiver Rcvr> state<Rcvr> connect(Rcvr&& rcvr) &&;-3- Precondition:
bool(handle)
istrue
.-4- Effects: Equivalent to:
return state<Rcvr>(exchange(handle, {}), std::forward<Rcvr>(recv));
task_scheduler::ts-sender::connect()
Section: 33.13.5 [exec.task.scheduler] Status: Tentatively Ready Submitter: Dietmar Kühl Opened: 2025-09-01 Last modified: 2025-10-17
Priority: Not Prioritized
View other active issues in [exec.task.scheduler].
View all other issues in [exec.task.scheduler].
View all issues with Tentatively Ready status.
Discussion:
The result of schedule(sched)
for a scheduler
sched
is only required to be movable. An object of
this type may need to be forwarded to an operation state constructor
by task_scheduler::ts-sender::connect
. Thus,
this function should be qualified with an rvalue reference.
[2025-10-17; Reflector poll.]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
Add an rvalue qualifier to the declaration of connect
in 33.13.5 [exec.task.scheduler] paragraph 8:
namespace std::execution { class task_scheduler::ts-sender { // exposition only public: using sender_concept = sender_t; template<receiver Rcvr> state<Rcvr> connect(Rcvr&& rcvr) &&; }; }
In the specification in 33.13.5 [exec.task.scheduler] paragraph 10 add an rvalue qualifier to connect
:
template<receiver Rcvr> state<Rcvr> connect(Rcvr&& rcvr) &&;-10- Effects: Let r be an object of a type that models receiver and whose completion handlers result in invoking the corresponding completion handlers of
rcvr
or copy thereof. Returns an object of typestate<Rcvr>
containing an operation state object initialized withconnect(SENDER(*this), std::move(r))
.
task
Section: 33.13.6.2 [task.class] Status: Tentatively Ready Submitter: Dietmar Kühl Opened: 2025-09-01 Last modified: 2025-10-17
Priority: Not Prioritized
View other active issues in [task.class].
View all other issues in [task.class].
View all issues with Tentatively Ready status.
Discussion:
The design discussion of task
describes defaults for
the two template parameters T
and Environment
of task
but these defaults are not reflected in the
synopsis of 33.13.6.2 [task.class].
This is an oversight and should be fixed. The default for
T
should be void
and the default for
Environment
should be env<>
(the
design paper used empty_env
but this struct
was replaced by the class template env
by P3325R5).
There could be a counter argument to defining a default for the
Environment
template parameter: this type is used to
determine various customizations of task
, e.g., the
allocator_type
, the scheduler_type
, and
the stop_source_type
. Leaving the type a required
argument means that a future standard could choose a possibly better
default than the types determined when the Environment
doesn't define them. On the other hand, a future standard could
provide a suitable alias with modified types under a different
name and/or a different namespace. Based on the LEWG discussion
on 2025-08-26 the direction is to add the default arguments.
[2025-10-17; Reflector poll.]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
Add default template arguments for task
for
T = void
and Environment = env<>
in the synopsis of 33.13.6.2 [task.class]:
namespace std::execution { template<class T = void, class Environment = env<>> class task { ... }; }
task::promise_type::return_value
default template parameterSection: 33.13.6.5 [task.promise] Status: Tentatively Ready Submitter: Dietmar Kühl Opened: 2025-09-01 Last modified: 2025-10-17
Priority: Not Prioritized
View other active issues in [task.promise].
View all other issues in [task.promise].
View all issues with Tentatively Ready status.
Discussion:
The template parameter V
of
task::promise_type::return_value
doesn't have a default
template argument specified. Specifying a default template argument of T
would enable use of co_return { ... }
which would be
consistent with normal return
statements. This feature
was not discussed in the design paper but based on the LEWG discussion
on 2025-08-26 it is considered to be more a bug fix than a new feature.
[2025-10-17; Reflector poll.]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
Add a default template argument of T
to the template
parameter V
of task::promise_type::return_value
in the synopsis of 33.13.6.5 [task.promise]:
namespace std::execution { template<class T, class Environment> class task<T, Environment>::promise_type { ... template<typename V = T> void return_value(V&& value); ... }; }
integral-constant-like
needs more remove_cvref_t
Section: 23.7.2.1 [span.syn] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2025-09-05 Last modified: 2025-10-17
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
P2781R9 tweaked the definition of
integral-constant-like
to work with constant_wrapper
,
like so:
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)>> && 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;
This was done so that cw<5>
models the concept,
but it needs an additional tweak so that
cw<true>
does not model it.
[2025-10-17; Reflector poll.]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
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_cvrefconst_t<decltype(T::value)>> && 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;
constexpr
for inplace_stop_token
and inplace_stop_source
Section: 32.3.8 [stoptoken.inplace] Status: Tentatively NAD Submitter: Lewis Baker Opened: 2025-08-28 Last modified: 2025-10-17
Priority: Not Prioritized
View all issues with Tentatively NAD status.
Discussion:
The inplace_stop_source::get_token()
member function is declared constexpr
,
but there are no constexpr
member-functions declared on inplace_stop_token
,
making the utility of being able to call this member function during constant
evaluation limited.
inplace_stop_token
also be declared constexpr
?
i.e. operator==
, swap()
, stop_possible()
and stop_requested()
.
The operator==
and stop_possible()
and swap()
member functions should be
able to be made constexpr
trivially as they are just required to compare/modify
pointers to the associated stop source.
The stop_requested()
member function is specified to be equivalent to calling
stop_requested()
on the associated inplace_stop_source
(if any), which is not
currently declared constexpr
primarily because its implementation requires
synchronisation/atomic operations.
Now that std::atomic
operations are now constexpr
, it may be possible/appropriate
for stop_requested()
on both inplace_stop_source
and inplace_stop_token
to also
be declared constexpr
.
[2025-10-17; Reflector poll. Status changed: New → Tentatively NAD.]
This allows constant-initializing a token, it's basically a constructor.
Other member functions don't need to be constexpr,
similar to how std::mutex::lock()
doesn't need to be constexpr
for constant-init of std::mutex
to be useful.
Proposed resolution:
This wording is relative to N5014.
[Drafting note:: This is the minimum proposed wording change. Additionally, consider adding
constexpr
to the declaration ofinplace_stop_token::stop_requested()
(in 32.3.8.1 [stoptoken.inplace.general] and 32.3.8.2 [stoptoken.inplace.mem]) and toinplace_stop_source::stop_requested()
(in 32.3.9.1 [stopsource.inplace.general] and 32.3.9.3 [stopsource.inplace.mem])]
Modify 32.3.8.1 [stoptoken.inplace.general], class inplace_stop_token
synopsis, as indicated:
namespace std { class inplace_stop_token { public: template<class CallbackFn> using callback_type = inplace_stop_callback<CallbackFn>; constexpr inplace_stop_token() = default; constexpr bool operator==(const inplace_stop_token&) const = default; // 32.3.8.2 [stoptoken.inplace.mem], member functions bool stop_requested() const noexcept; constexpr bool stop_possible() const noexcept; constexpr void swap(inplace_stop_token&) noexcept; private: const inplace_stop_source* stop-source = nullptr; // exposition only }; }
Modify 32.3.8.2 [stoptoken.inplace.mem] as indicated:
[Drafting note:: As a drive-by fix this adds the missing return type
bool
to thestop_possible()
prototype]
constexpr void swap(inplace_stop_token& rhs) noexcept;[…]-1- Effects: Exchanges the values of
stop-source
andrhs.stop-source
.constexpr bool stop_possible() const noexcept;-4- Returns:
stop-source != nullptr
.
expected
may be ill-formedSection: 22.8.6.8 [expected.object.eq], 22.8.7.8 [expected.void.eq] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2025-09-06 Last modified: 2025-10-16
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
These comparison functions all explicitly static_cast
the result of the underlying comparison to
bool
. However, the Constraints only require the implicit conversion, not the explicit one
(i.e., "convertible to bool
" rather than "models boolean-testable
").
#include <expected>
struct E1 {};
struct E2 {};
struct Bool {
operator bool() const;
explicit operator bool() = delete;
};
Bool operator==(E1, E2);
int main() {
std::unexpected e1{E1{}};
std::unexpected e2{E2{}};
return std::expected<int, E1>{e1} == e2; // fire
}
It is reasonable to specify return consistency with actual Constraints.
[2025-10-16; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Related to LWG 4366(i), but the wording styles are inconsistent.
optional
uses "Effects: Equivalent to ..." and expected
just uses
Returns:.
Proposed resolution:
This wording is relative to N5014.
Modify 22.8.6.8 [expected.object.eq] as indicated:
template<class T2> friend constexpr bool operator==(const expected& x, const T2& v);-3- Constraints:
[Note 1:T2
is not a specialization ofexpected
. The expression*x == v
is well-formed and its result is convertible tobool
.T
need not be Cpp17EqualityComparable. — end note] -4- Returns: Ifx.has_value()
istrue
,; otherwise
&& static_cast<bool>(*x == v)false
.template<class E2> friend constexpr bool operator==(const expected& x, const unexpected<E2>& e);-5- Constraints: The expression
-6- Returns: Ifx.error() == e.error()
is well-formed and its result is convertible tobool
.!x.has_value()
istrue
,; otherwise
&& static_cast<bool>(x.error() == e.error())false
.
Modify 22.8.7.8 [expected.void.eq] as indicated:
template<class T2, class E2> requires is_void_v<T2> friend constexpr bool operator==(const expected& x, const expected<T2, E2>& y);-1- Constraints: The expression
-2- Returns: Ifx.error() == y.error()
is well-formed and its result is convertible tobool
.x.has_value()
does not equaly.has_value()
,false
; otherwise ifx.has_value()
istrue
,true
; otherwise.
|| static_cast<bool>(x.error() == y.error())template<class E2> friend constexpr bool operator==(const expected& x, const unexpected<E2>& e);-3- Constraints: The expression
-4- Returns: Ifx.error() == e.error()
is well-formed and its result is convertible tobool
.!x.has_value()
istrue
,; otherwise
&& static_cast<bool>(x.error() == e.error())false
.
optional<T>
to T
may be ill-formedSection: 22.5.9 [optional.comp.with.t] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2025-09-06 Last modified: 2025-10-16
Priority: Not Prioritized
View all other issues in [optional.comp.with.t].
View all issues with Tentatively Ready status.
Discussion:
When comparing an optional
with its value type, the current wording specifies that the result is the
ternary expression of x.has_value() ? *x == v : false
, where *x == v
returns a result that can be
implicitly converted to bool
.
bool
(which is common), the ternary operation
will be ill-formed due to ambiguity (demo):
#include <optional>
struct Bool {
Bool(bool);
operator bool() const;
};
struct S {
Bool operator==(S) const;
};
int main() {
return std::optional<S>{} == S{}; // fire
}
[2025-10-16; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
"Alternatively could keep the conditional operator but cast one side to bool
,
but that would do an explicit conversion, which might not be what we want."
"Should just require boolean-testable."
Proposed resolution:
This wording is relative to N5014.
Modify 22.5.9 [optional.comp.with.t] as indicated:
template<class T, class U> constexpr bool operator==(const optional<T>& x, const U& v);-1- Constraints:
[Note 1:U
is not a specialization ofoptional
. The expression*x == v
is well-formed and its result is convertible tobool
.T
need not be Cpp17EqualityComparable. — end note] -2- Effects: Equivalent to:return x.has_value() ? *x == v : false;
if (x.has_value()) return *x == v; return false;template<class T, class U> constexpr bool operator==(const T& v, const optional<U>& x);-3- Constraints:
-4- Effects: Equivalent to:T
is not a specialization ofoptional
. The expressionv == *x
is well-formed and its result is convertible tobool
.return x.has_value() ? v == *x : false;
if (x.has_value()) return v == *x; return false;template<class T, class U> constexpr bool operator!=(const optional<T>& x, const U& v);-5- Constraints:
-6- Effects: Equivalent to:U
is not a specialization ofoptional
. The expression*x != v
is well-formed and its result is convertible tobool
.return x.has_value() ? *x != v : true;
if (x.has_value()) return *x != v; return true;template<class T, class U> constexpr bool operator!=(const T& v, const optional<U>& x);-7- Constraints:
-8- Effects: Equivalent to:T
is not a specialization ofoptional
. The expressionv != *x
is well-formed and its result is convertible tobool
.return x.has_value() ? v != *x : true;
if (x.has_value()) return v != *x; return true;template<class T, class U> constexpr bool operator<(const optional<T>& x, const U& v);-9- Constraints:
-10- Effects: Equivalent to:U
is not a specialization ofoptional
. The expression*x < v
is well-formed and its result is convertible tobool
.return x.has_value() ? *x < v : true;
if (x.has_value()) return *x < v; return true;template<class T, class U> constexpr bool operator<(const T& v, const optional<U>& x);-11- Constraints:
-12- Effects: Equivalent to:T
is not a specialization ofoptional
. The expressionv < *x
is well-formed and its result is convertible tobool
.return x.has_value() ? v < *x : false;
if (x.has_value()) return v < *x; return false;template<class T, class U> constexpr bool operator>(const optional<T>& x, const U& v);-13- Constraints:
-14- Effects: Equivalent to:U
is not a specialization ofoptional
. The expression*x > v
is well-formed and its result is convertible tobool
.return x.has_value() ? *x > v : false;
if (x.has_value()) return *x > v; return false;template<class T, class U> constexpr bool operator>(const T& v, const optional<U>& x);-15- Constraints:
-16- Effects: Equivalent to:T
is not a specialization ofoptional
. The expressionv > *x
is well-formed and its result is convertible tobool
.return x.has_value() ? v > *x : true;
if (x.has_value()) return v > *x; return true;template<class T, class U> constexpr bool operator<=(const optional<T>& x, const U& v);-17- Constraints:
-18- Effects: Equivalent to:U
is not a specialization ofoptional
. The expression*x <= v
is well-formed and its result is convertible tobool
.return x.has_value() ? *x <= v : true;
if (x.has_value()) return *x <= v; return true;template<class T, class U> constexpr bool operator<=(const T& v, const optional<U>& x);-19- Constraints:
-20- Effects: Equivalent to:T
is not a specialization ofoptional
. The expressionv <= *x
is well-formed and its result is convertible tobool
.return x.has_value() ? v <= *x : false;
if (x.has_value()) return v <= *x; return false;template<class T, class U> constexpr bool operator>=(const optional<T>& x, const U& v);-21- Constraints:
-22- Effects: Equivalent to:U
is not a specialization ofoptional
. The expression*x >= v
is well-formed and its result is convertible tobool
.return x.has_value() ? *x >= v : false;
if (x.has_value()) return *x >= v; return false;template<class T, class U> constexpr bool operator>=(const T& v, const optional<U>& x);-23- Constraints:
-24- Effects: Equivalent to:T
is not a specialization ofoptional
. The expressionv >= *x
is well-formed and its result is convertible tobool
.return x.has_value() ? v >= *x : true;
if (x.has_value()) return v >= *x; return true;
Section: 23.7.3.4.8.1 [mdspan.layout.leftpad.overview], 23.7.3.4.9.1 [mdspan.layout.rightpad.overview] Status: Tentatively Ready Submitter: Luc Grosheintz Opened: 2025-09-09 Last modified: 2025-10-17
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
Two new layouts were added to <mdspan>
in C++26. Both have a template
parameter size_t PaddingValue
. This value is allowed to be std::dynamic_extent
to signal that the padding value isn't known at compile time.
PaddingValue
is representable as a value of index_type
.
Since std::dynamic_extent
is defined as size_t(-1)
(in 23.7.2.1 [span.syn])
this immediately prohibits all dynamically padded layout mappings for
any index_type
for which:
numeric_limit<index_type>::max() < numeric_limit<size_t>::max()
One example is int
on a 64-bit system.
rank <= 1
, even though in that case the
PaddingValue
has no other effect. Hence, the Mandates: element could
be weakened further.
[2025-10-17; Reflector poll.]
Set status to Tentatively Ready after six votes in favour during reflector poll.
"This matches the wording in 23.7.3.3.1 [mdspan.extents.overview] 1.2"
Proposed resolution:
This wording is relative to N5014.
Modify 23.7.3.4.8.1 [mdspan.layout.leftpad.overview] as indicated:
-5- Mandates:
(5.1) — […]
(5.2) — if
padding_value
is not equal todynamic_extent
, thenpadding_value
is representable as a value of typeindex_type
.(5.3) — […]
(5.4) — […]
Modify 23.7.3.4.9.1 [mdspan.layout.rightpad.overview] as indicated:
-5- Mandates:
(5.1) — […]
(5.2) — if
padding_value
is not equal todynamic_extent
, thenpadding_value
is representable as a value of typeindex_type
.(5.3) — […]
(5.4) — […]
simd::basic_vec
constructorSection: 29.10.7.2 [simd.ctor] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2025-09-29 Last modified: 2025-10-17
Priority: Not Prioritized
View other active issues in [simd.ctor].
View all other issues in [simd.ctor].
View all issues with Tentatively NAD status.
Discussion:
The broadcasting, generator-based, and range constructors of simd::basic_vec
all take a single
argument, and their constraints are not mutually exclusive.
value_type
, this will lead to ambiguity:
#include <simd>
struct S {
operator double() const; // basic_vec(U&& value)
double operator()(int) const; // basic_vec(G&& gen)
double* begin() const; // basic_vec(R&& r, flags<Flags...> = {});
double* end() const;
constexpr static int size() { return 2; }
};
int main() {
std::simd::vec<double> simd(S{}); // error: call of overloaded 'basic_simd(S)' is ambiguous
}
Do we need more constraints, similar to the one in string_view(R&& r)
that requires
R
not to be convertible to const char*
, to make the above work, i.e., only invoke the
broadcasting constructor?
[2025-10-17; Reflector poll. Status changed: New → Tentatively NAD.]
Users of such types should do disambiguation explicitly, basic_vec
should not guess what they mean.
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.7.2 [simd.ctor] as indicated:
template<class G> constexpr explicit basic_vec(G&& gen);[…]-8- Let
-9- Constraints:Fromi
denote the typedecltype(gen(integral_constant<simd-size-type, i>()))
.
(9.?) —
constructible_from<value_type, G>
isfalse
.(9.?) —
Fromi
satisfiesconvertible_to<value_type>
for alli
in the range of [0, size()
). In addition, for all i in the range of [0, size()
), ifFromi
is an arithmetic type, conversion fromFromi
tovalue_type
is value-preserving.template<class R, class... Flags> constexpr basic_vec(R&& r, flags<Flags...> = {}); template<class R, class... Flags> constexpr basic_vec(R&& r, const mask_type& mask, flags<Flags...> = {});-12- Let mask be
-13- Constraints:mask_type(true)
for the overload with nomask
parameter.
(13.1) —
R
modelsranges::contiguous_range
andranges::sized_range
,(13.2) —
ranges::size(r)
is a constant expression,and(13.3) —
ranges::size(r)
is equal tosize()
.,(13.?) —
constructible_from<value_type, R>
isfalse
, and(13.?) —
r(integral_constant<simd-size-type, 0>())
is not a valid expression.
enable_nonlocking_formatter_optimization
should be disabled for container adaptorsSection: 23.6.2 [queue.syn], 23.6.5 [stack.syn] Status: Tentatively Ready Submitter: Tomasz Kamiński Opened: 2025-10-02 Last modified: 2025-10-17
Priority: 2
View all issues with Tentatively Ready status.
Discussion:
As the standard currently defines formatters for queue
, prioriy_queue
, and stack
enable_nonlocking_formatter_optimization
is specialized to true
for these adaptors per
28.5.6.4 [format.formatter.spec] p3:
Unless specified otherwise, for each type
T
for which a formatter specialization is provided by the library, each of the headers provides the following specialization:template<> inline constexpr bool enable_nonlocking_formatter_optimization<T> = true;
However, formatting an adaptor requires formatting of the underlying range
in terms of ranges::ref_view
, and we disable the nonlocking_optimizations for all ranges, including ranges::ref_view
.
flat_set
, flat_map
adaptors, which are
also ranges, but unlike stack
etc. they do not have a specialized formatter.
They use the formatter
specialization for ranges and we already disable the
optimization for that formatter.
The proposed wording has recently been implemented in
gcc's libstdc++.
[2025-10-14; Reflector poll]
Set priority to 2 after reflector poll.
This is a duplicate of LWG 4146(i), with a different proposed resolution.
[2025-10-17; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 23.6.2 [queue.syn], header <queue>
synopsis, as indicated:
[…] // 23.6.13 [container.adaptors.format], formatter specialization for queue template<class charT, class T, formattable<charT> Container> struct formatter<queue<T, Container>, charT>; template<class T, class Container> constexpr bool enable_nonlocking_formatter_optimization<queue<T, Container>> = false; // 23.6.4 [priority.queue], class template priority_queue template<class T, class Container = vector<T>, class Compare = less<typename Container::value_type>> class priority_queue; […] // 23.6.13 [container.adaptors.format], formatter specialization for priority_queue template<class charT, class T, formattable<charT> Container, class Compare> struct formatter<priority_queue<T, Container, Compare>, charT>; template<class T, class Container, class Compare> constexpr bool enable_nonlocking_formatter_optimization<priority_queue<T, Container, Compare>> = false; […]
Modify 23.6.5 [stack.syn], header <stack>
synopsis, as indicated:
[…] // 23.6.13 [container.adaptors.format], formatter specialization for stack template<class charT, class T, formattable<charT> Container> struct formatter<stack<T, Container>, charT>; template<class T, class Container> constexpr bool enable_nonlocking_formatter_optimization<stack<T, Container>> = false; […]
enable_nonlocking_formatter_optimization
for pair
and tuple
needs remove_cvref_t
Section: 28.5.9 [format.tuple] Status: Tentatively Ready Submitter: Tomasz Kamiński Opened: 2025-10-02 Last modified: 2025-10-17
Priority: Not Prioritized
View all issues with Tentatively Ready status.
Discussion:
The enable_nonlocking_formatter_optimization
variable template is specialized only for cv-unqualified
types. However, the specialization for pair
and tuple
does not remove the references and
cv-qualifiers from the elements:
template<class... Ts> constexpr bool enable_nonlocking_formatter_optimization<pair-or-tuple<Ts...>> = (enable_nonlocking_formatter_optimization<Ts> && ...);
As consequence pair<const std::string, int>
or
pair<const std::string&, int&>
(map
and flat_map
reference types)
will not use unbuffered prints.
[2025-10-17; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 28.5.9 [format.tuple] as indicated:
-1- For each of
pair
andtuple
, the library provides the following formatter specialization wherepair-or-tuple
is the name of the template:namespace std { […] template<class... Ts> constexpr bool enable_nonlocking_formatter_optimization<pair-or-tuple<Ts...>> = (enable_nonlocking_formatter_optimization<remove_cvref_t<Ts>> && ...); }
simd::basic_vec
CTAD misses difference type castingSection: 29.10.7.2 [simd.ctor] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2025-10-04 Last modified: 2025-10-17
Priority: Not Prioritized
View other active issues in [simd.ctor].
View all other issues in [simd.ctor].
View all issues with Tentatively Ready status.
Discussion:
Currently, basic_vec
can take an object r
of range type R
whose size is a
constant expression and deduced to vec<ranges::range_value_t<R>, ranges::size(r)>
.
R
has a an integer-class type size which cannot
be implicitly converted to simd-size-type
, which is a signed integer type.
It is necessary to perform difference type casting here, and the narrowing
conversion will still correctly be rejected due to the constructor's constraints.
[2025-10-17; Reflector poll]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.7.2 [simd.ctor] as indicated:
template<class R, class... Ts> basic_vec(R&& r, Ts...) -> see below;-17- Constraints:
(17.1) —
R
modelsranges::contiguous_range
andranges::sized_range
, and(17.2) —
ranges::size(r)
is a constant expression.-18- Remarks: The deduced type is equivalent to
vec<ranges::range_value_t<R>, static_cast<simd-size-type>(ranges::size(r))>
constexpr-wrapper-like
needs remove_cvref_t
in simd::basic_vec
constructorSection: 29.10.7.2 [simd.ctor] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2025-10-05 Last modified: 2025-10-17
Priority: Not Prioritized
View other active issues in [simd.ctor].
View all other issues in [simd.ctor].
View all issues with Tentatively Ready status.
Discussion:
decltype(From::value)
would be const int&
if From
is a type of std::cw<42>
,
so the reference also needs to be removed for checking the arithmetic type.
[2025-10-17; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.7.2 [simd.ctor] as indicated:
template<class U> constexpr explicit(see below) basic_vec(U&& value) noexcept;-1- Let
[…] -4- Remarks: The expression insideFrom
denote the typeremove_cvref_t<U>
.explicit
evaluates tofalse
if and only ifU
satisfiesconvertible_to<value_type>
, and either
(4.1) —
From
is not an arithmetic type and does not satisfyconstexpr-wrapper-like
,(4.2) —
From
is an arithmetic type and the conversion fromFrom
tovalue_type
is value-preserving (29.10.1 [simd.general]), or(4.3) —
From
satisfiesconstexpr-wrapper-like
,remove_cvref_t
is an arithmetic type, andremove_const_t<decltype(From::value)>From::value
is representable byvalue_type
.