# C++ Standard Library Issues to be moved in [INSERT CURRENT MEETING HERE]

Doc. no. | R0165???
Date: | Revised 2022-11-24 at 09:59:48 UTC
Project: | Programming Language C++
Reply to: | Jonathan Wakely <lwgchair@gmail.com>

## Ready Issues

### 2195(i). Missing constructors for `match_results`

Section: 32.9 [re.results] Status: Ready Submitter: Daniel Krügler Opened: 2012-10-06 Last modified: 2022-11-10

Priority: 3

View all other issues in [re.results].

View all issues with Ready status.

Discussion:

The requirement expressed in 32.9 [re.results] p2

> The class template `match_results` shall satisfy the requirements of an allocator-aware container and of a sequence container, as specified in 24.2.4 [sequence.reqmts], except that only operations defined for const-qualified sequence containers are supported.

can be read to require the existence of the described constructors from as well, but they do not exist in the synopsis.

The missing sequence constructors are:

>     match_results(initializer_list<value_type>);
>     match_results(size_type, const value_type&);
>     template<class InputIterator> match_results(InputIterator, InputIterator);

The missing allocator-aware container constructors are:

>     match_results(const match_results&, const Allocator&);
>     match_results(match_results&&, const Allocator&);

It should be clarified, whether (a) constructors are an exception of above mentioned operations or (b) whether at least some of them (like those accepting a `match_results` value and an allocator) should be added.

As visible in several places of the standard (including the core language), constructors seem usually to be considered as "operations" and they certainly can be invoked for const-qualified objects.

The below given proposed resolution applies only the minimum necessary fix, i.e. it excludes constructors from above requirement.

[2013-04-20, Bristol]

Check current implementations to see what they do and, possibly, write a paper.

[2013-09 Chicago]

Ask Daniel to update the proposed wording to include the allocator copy and move constructors.

[2014-01-18 Daniel changes proposed resolution]

Previous resolution from Daniel [SUPERSEDED]:

> 1. Change 32.9 [re.results] p2 as indicated:

>    > The class template `match_results` shall satisfy the requirements of an allocator-aware container and of a sequence container, as specified in 24.2.4 [sequence.reqmts], except that only operations defined for const-qualified sequence containers that are not constructors are supported.

[2015-05-06 Lenexa]

MC passes important knowledge to EF.

VV, RP: Looks good.

TK: Second form should be conditionally noexcept

JY: Sequence constructors are not here, but mentioned in the issue writeup. Why?

TK: That would have been fixed by the superseded wording.

JW: How does this interact with Mike Spertus' allocator-aware regexes? [...] Perhaps it doesn't.

JW: Can't create match_results, want both old and new resolution.

JY: It's problematic that users can't create these, but not this issue.

VV: Why conditional noexcept?

MC: Allocator move might throw.

JW: Update superseded wording to "only non-constructor operations that are"?

MC: Only keep superseded, but append "and the means of constructing match_results are limited to [...]"?

JY: Bullet 4 paragraph 2 needs to address the allocator constructor.

Assigned to JW for drafting.

[2015-10, Kona Saturday afternoon]

STL: I want Mike Spertus to be aware of this issue.

Previous resolution from Daniel [SUPERSEDED]:

> This wording is relative to N3936.

> 1. Change 32.9 [re.results] p4, class template `match_results` synopsis, as indicated:

>    >     […]
>    >     // 28.10.1, construct/copy/destroy:
>    >     explicit match_results(const Allocator& a = Allocator());
>    >     match_results(const match_results& m);
>    >     match_results(const match_results& m, const Allocator& a);
>    >     match_results(match_results&& m) noexcept;
>    >     match_results(match_results&& m, const Allocator& a) noexcept;
>    >     […]

> 2. Change 32.9.2 [re.results.const] as indicated: [Drafting note: Paragraph 6 as currently written, makes not much sense, because the `noexcept` does not allow any exception to propagate. Further-on, the allocator requirements do not allow for throwing move constructors. Deleting it seems to be near to editorial — end drafting note]

>    >     match_results(const match_results& m);
>    >     match_results(const match_results& m, const Allocator& a);

>    > > -4- Effects: Constructs an object of class `match_results`, as a copy of `m`.

>    >     match_results(match_results&& m) noexcept;
>    >     match_results(match_results&& m, const Allocator& a) noexcept;

>    > > -5- Effects: Move-constructs an object of class `match_results` from `m` satisfying the same postconditions as Table 142. Additionally For the first form, the stored `Allocator` value is move constructed from `m.get_allocator()`.

>    > > -6- Throws: Nothing if the allocator's move constructor throws nothing.

[2019-03-27 Jonathan updates proposed resolution]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4810.

> These edits overlap with the proposed resolution of 2191 but it should be obvious how to resolve the conflicts. Both resolutions remove the word "Additionally" from p4. Issue 2191 removes the entire Throws: element in p5 but this issue replaces it with different text that applies to the new constructor only.

> 1. Change 32.9 [re.results] p4, class template `match_results` synopsis, as indicated:

>    >     […]
>    >     // 30.10.1, construct/copy/destroy:
>    >     explicit match_results(const Allocator& a = Allocator());
>    >     match_results(const match_results& m);
>    >     match_results(const match_results& m, const Allocator& a);
>    >     match_results(match_results&& m) noexcept;
>    >     match_results(match_results&& m, const Allocator& a);
>    >     […]

> 2. Change 32.9.2 [re.results.const] as indicated:

>    >     match_results(const match_results& m);
>    >     match_results(const match_results& m, const Allocator& a);

>    > > -3- Effects: Constructs an object of class `match_results`, as a copy of `m`. For the second form, the stored `Allocator` value is constructed from `a`.

>    >     match_results(match_results&& m) noexcept;
>    >     match_results(match_results&& m, const Allocator& a);

>    > > -4- Effects: Move-constructs an object of class `match_results` from `m` satisfying the same postconditions as Table 128. Additionally For the first form, the stored `Allocator` value is move constructed from `m.get_allocator()`. For the second form, the stored `Allocator` value is constructed from `a`.

>    > > -6- Throws: Nothing. The second form throws nothing if `a == m.get_allocator()`.

[2022-11-06; Daniel syncs wording with recent working draft]

To ensure that all constructors are consistent in regard to the information about how the stored allocator is constructed, more wording is added. This harmonizes with the way how we specify the individual container constructors (Such as `vector`) even though 24.2.2.5 [container.alloc.reqmts] already provides some guarantees. For the copy-constructor we intentionally refer to 24.2.2.2 [container.reqmts] so that we don't need to repeat what is said there.

[Kona 2022-11-08; Move to Ready]

Proposed resolution:

This wording is relative to N4917.

1. Change 32.9 [re.results], class template `match_results` synopsis, as indicated:

   >     […]
   >     // 32.9.2 [re.results.const], construct/copy/destroy:
   >     match_results() : match_results(Allocator()) {}
   >     explicit match_results(const Allocator& a);
   >     match_results(const match_results& m);
   >     match_results(const match_results& m, const Allocator& a);
   >     match_results(match_results&& m) noexcept;
   >     match_results(match_results&& m, const Allocator& a);
   >     […]

2. Change 32.9.2 [re.results.const] as indicated:

   >     explicit match_results(const Allocator& a);

   > > -?- Effects: The stored `Allocator` value is constructed from `a`.

   > > -2- Postconditions: `ready()` returns `false`. `size()` returns `0`.

   >     match_results(const match_results& m);
   >     match_results(const match_results& m, const Allocator& a);

   > > -?- Effects: For the first form, the stored `Allocator` value is obtained as specified in 24.2.2.2 [container.reqmts]. For the second form, the stored `Allocator` value is constructed from `a`.

   > > -3- Postconditions: As specified in Table 142 [tab:re.results.const].

   >     match_results(match_results&& m) noexcept;
   >     match_results(match_results&& m, const Allocator& a);

   > > -4- Effects: For the first form, t The stored `Allocator` value is move constructed from `m.get_allocator()`. For the second form, the stored `Allocator` value is constructed from `a`.

   > > -5- Postconditions: As specified in Table 142 [tab:re.results.const].

   > > -?- Throws: The second form throws nothing if `a == m.get_allocator()` is `true`.

### 2295(i). Locale name when the provided `Facet` is a `nullptr`

Section: 30.3.1.3 [locale.cons] Status: Ready Submitter: Juan Soulie Opened: 2013-09-04 Last modified: 2022-11-09

Priority: 3

View other active issues in [locale.cons].

View all other issues in [locale.cons].

View all issues with Ready status.

Discussion:

30.3.1.3 [locale.cons] p14 ends with:

> "[…] If `f` is null, the resulting object is a copy of `other`."

but the next line p15 says:

> "Remarks: The resulting locale has no name."

But both can't be true when `other` has a name and `f` is null.

I've tried it on two implementations (MSVC,GCC) and they are inconsistent with each other on this.

Daniel Krügler:

As currently written, the Remarks element applies unconditionally for all cases and thus should "win". The question arises whether the introduction of this element by LWG 424 had actually intended to change the previous Note to a Remarks element. In either case the wording should be improved to clarify this special case.

[2022-02-14; Daniel comments]

This issue seems to have some overlap with LWG 3676 so both should presumably be resolved in a harmonized way.

[2022-11-01; Jonathan provides wording]

This also resolves 3673 and 3676.

[2022-11-04; Jonathan revises wording after feedback]

Revert an incorrect edit to p8, which was incorrectly changed to:

"If `cats` is equal to `locale::none`, the resulting locale has the same name as `locale(std_name)`. Otherwise, the locale has a name if and only if `other` has a name."

[Kona 2022-11-08; Move to Ready status]

Proposed resolution:

This wording is relative to N4917.

1. Modify 30.3.1.3 [locale.cons] as indicated:

   > >     explicit locale(const char* std_name);

   > > -2- Effects: Constructs a locale using standard C locale names, e.g., `"POSIX"`. The resulting locale implements semantics defined to be associated with that name.

   > > -3- Throws: `runtime_error` if the argument is not valid, or is null.

   > > -4- Remarks: The set of valid string argument values is `"C"`, `""`, and any implementation-defined values.

   > >     explicit locale(const string& std_name);

   > > -5- Effects: The same as Equivalent to `locale(std_name.c_str())`.

   > >     locale(const locale& other, const char* std_name, category cats);

   > > -?- Preconditions: `cats` is a valid `category` value (30.3.1.2.1 [locale.category]).

   > > -6- Effects: Constructs a locale as a copy of `other` except for the facets identified by the `category` argument, which instead implement the same semantics as `locale(std_name)`.

   > > -7- Throws: `runtime_error` if the second argument is not valid, or is null.

   > > -8- Remarks: The locale has a name if and only if `other` has a name.

   > >     locale(const locale& other, const string& std_name, category cats);

   > > -9- Effects: The same as Equivalent to `locale(other, std_name.c_str(), cats)`.

   > >     template<class Facet> locale(const locale& other, Facet* f);

   > > -10- Effects: Constructs a locale incorporating all facets from the first argument except that of type `Facet`, and installs the second argument as the remaining facet. If `f` is null, the resulting object is a copy of `other`.

   > > -11- Remarks: If `f` is null, the resulting locale has the same name as `other`. Otherwise, the The resulting locale has no name.

   > >     locale(const locale& other, const locale& one, category cats);

   > > -?- Preconditions: `cats` is a valid `category` value.

   > > -12- Effects: Constructs a locale incorporating all facets from the first argument except for those that implement `cats`, which are instead incorporated from the second argument.

   > > -13- Remarks: If `cats` is equal to `locale::none`, the resulting locale has a name if and only if the first argument has a name. Otherwise, the The locale has a name if and only if the first two arguments both have names.

### 3032(i). `ValueSwappable` requirement missing for `push_heap` and `make_heap`

Section: 27.8.8 [alg.heap.operations] Status: Ready Submitter: Robert Douglas Opened: 2017-11-08 Last modified: 2022-11-10

Priority: 3

View all other issues in [alg.heap.operations].

View all issues with Ready status.

Discussion:

In discussion of D0202R3 in Albuquerque, it was observed that `pop_heap` and `sort_heap` had `constexpr` removed for their requirement of `ValueSwappable`. It was then observed that `push_heap` and `make_heap` were not similarly marked as having the `ValueSwappable` requirement. The room believed this was likely a specification error, and asked to open an issue to track it.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 3; Marshall to investigate

Previous resolution [SUPERSEDED]:

> This wording is relative to N4700.

> 1. Change 27.8.8.2 [push.heap] as indicated:

>    >     template<class RandomAccessIterator>
>    >       void push_heap(RandomAccessIterator first, RandomAccessIterator last);
>    >     template<class RandomAccessIterator, class Compare>
>    >       void push_heap(RandomAccessIterator first, RandomAccessIterator last,
>    >                      Compare comp);

>    > > -1- Requires: The range `[first, last - 1)` shall be a valid heap. `RandomAccessIterator` shall satisfy the requirements of `ValueSwappable` (16.4.4.3 [swappable.requirements]). The type of `*first` shall satisfy the `MoveConstructible` requirements (Table 23) and the `MoveAssignable` requirements (Table 25).

> 2. Change 27.8.8.4 [make.heap] as indicated:

>    >     template<class RandomAccessIterator>
>    >       void make_heap(RandomAccessIterator first, RandomAccessIterator last);
>    >     template<class RandomAccessIterator, class Compare>
>    >       void make_heap(RandomAccessIterator first, RandomAccessIterator last,
>    >                      Compare comp);

>    > > -1- Requires: `RandomAccessIterator` shall satisfy the requirements of `ValueSwappable` (16.4.4.3 [swappable.requirements]). The type of `*first` shall satisfy the `MoveConstructible` requirements (Table 23) and the `MoveAssignable` requirements (Table 25).

[2022-11-06; Daniel comments and syncs wording with recent working draft]

For reference, the finally accepted paper was P0202R3 and the constexpr-ification of swap-related algorithms had been realized later by P0879R0 after resolution of CWG 1581 and more importantly CWG 1330.

[Kona 2022-11-09; Move to Ready]

Proposed resolution:

This wording is relative to N4917.

1. Change 27.8.8.2 [push.heap] as indicated:

   >     template<class RandomAccessIterator>
   >       constexpr void push_heap(RandomAccessIterator first, RandomAccessIterator last);

   >     template<class RandomAccessIterator, class Compare>
   >       constexpr void push_heap(RandomAccessIterator first, RandomAccessIterator last,
   >                                Compare comp);

   >     template<random_access_iterator I, sentinel_for<I> S, class Comp = ranges::less,
   >              class Proj = identity>
   >       requires sortable<I, Comp, Proj>
   >       constexpr I
   >         ranges::push_heap(I first, S last, Comp comp = {}, Proj proj = {});
   >     template<random_access_range R, class Comp = ranges::less, class Proj = identity>
   >       requires sortable<iterator_t<R>, Comp, Proj>
   >       constexpr borrowed_iterator_t<R>
   >         ranges::push_heap(R&& r, Comp comp = {}, Proj proj = {});

   > > -1- Let `comp` be `less{}` and `proj` be `identity{}` for the overloads with no parameters by those names.

   > > -2- Preconditions: The range `[first, last - 1)` is a valid heap with respect to `comp` and `proj`. For the overloads in namespace `std`, `RandomAccessIterator` meets the Cpp17ValueSwappable requirements (16.4.4.3 [swappable.requirements]) and the type of `*first` meets the Cpp17MoveConstructible requirements (Table 32) and the Cpp17MoveAssignable requirements (Table 34).

   > > -3- Effects: Places the value in the location `last - 1` into the resulting heap `[first, last)`.

   > > -4- Returns: `last` for the overloads in namespace `ranges`.

   > > -5- Complexity: At most log(`last - first`) comparisons and twice as many projections.

2. Change 27.8.8.4 [make.heap] as indicated:

   >     template<class RandomAccessIterator>
   >       constexpr void make_heap(RandomAccessIterator first, RandomAccessIterator last);

   >     template<class RandomAccessIterator, class Compare>
   >       constexpr void make_heap(RandomAccessIterator first, RandomAccessIterator last,
   >                                Compare comp);

   >     template<random_access_iterator I, sentinel_for<I> S, class Comp = ranges::less,
   >              class Proj = identity>
   >       requires sortable<I, Comp, Proj>
   >       constexpr I
   >         ranges::make_heap(I first, S last, Comp comp = {}, Proj proj = {});
   >     template<random_access_range R, class Comp = ranges::less, class Proj = identity>
   >       requires sortable<iterator_t<R>, Comp, Proj>
   >       constexpr borrowed_iterator_t<R>
   >         ranges::make_heap(R&& r, Comp comp = {}, Proj proj = {});

   > > -1- Let `comp` be `less{}` and `proj` be `identity{}` for the overloads with no parameters by those names.

   > > -2- Preconditions: For the overloads in namespace `std`, `RandomAccessIterator` meets the Cpp17ValueSwappable requirements (16.4.4.3 [swappable.requirements]) and the type of `*first` meets the Cpp17MoveConstructible (Table 32) and Cpp17MoveAssignable (Table 34) requirements.

   > > -3- Effects: Constructs a heap with respect to `comp` and `proj` out of the range `[first, last)`.

   > > -4- Returns: `last` for the overloads in namespace `ranges`.

   > > -5- Complexity: At most 3(`last - first`) comparisons and twice as many projections.

### 3085(i). `char_traits::copy` precondition too weak

Section: 23.2.2 [char.traits.require] Status: Ready Submitter: Jonathan Wakely Opened: 2018-03-16 Last modified: 2022-11-11

Priority: 2

View other active issues in [char.traits.require].

View all other issues in [char.traits.require].

View all issues with Ready status.

Discussion:

Table 54, Character traits requirements, says that `char_traits::move` allows the ranges to overlap, but `char_traits::copy` requires that `p` is not in the range `[s, s + n)`. This appears to be an attempt to map to the requirements of `memmove` and `memcpy` respectively, allowing those to be used to implement the functions, however the requirements for `copy` are weaker than those for `memcpy`. The C standard says for `memcpy` "If copying takes place between objects that overlap, the behavior is undefined" which is a stronger requirement than the start of the source range not being in the destination range.

All of libstdc++, libc++ and VC++ simply use `memcpy` for `char_traits<char>::copy`, resulting in undefined behaviour in this example:

>     char p[] = "abc";
>     char* s = p + 1;
>     std::char_traits<char>::copy(s, p, 2);
>     assert(std::char_traits<char>::compare(p, "aab", 3) == 0);

If the intention is to allow `memcpy` as a valid implementation then the precondition is wrong (unfortunately nobody realized this when fixing `char_traits::move` in LWG DR 7). If the intention is to require `memmove` then it is strange to have separate `copy` and `move` functions that both use `memmove`.

N.B. `std::copy` and `std::copy_backward` are not valid implementations of `char_traits::copy` either, due to different preconditions.

Changing the precondition implicitly applies to `basic_string::copy` (23.4.3.7.7 [string.copy]), and `basic_string_view::copy` (23.3.3.8 [string.view.ops]), which are currently required to support partially overlapping ranges:

>     std::string s = "abc";
>     s.copy(s.data() + 1, s.length() - 1);
>     assert(s == "aab");

[2018-04-03 Priority set to 2 after discussion on the reflector.]

[2018-08-23 Batavia Issues processing]

No consensus for direction; revisit in San Diego. Status to Open.

[2022-04-25; Daniel rebases wording on N4910]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4910.

> Option A:

> > 1. Edit 23.2.2 [char.traits.require], Table 75 — "Character traits requirements" [tab:char.traits.req], as indicated:

> >    > Table 75 — Character traits requirements [tab:char.traits.req]
> >    > Expression | Return type | Assertion/note pre/post-condition | Complexity
> >    > --- | --- | --- | ---
> >    > `[…]`
> >    > `X::copy(s,p,n)` | `X::char_type*` | Preconditions: `p` not in `[s,s+n)`The ranges `[p,p+n)` and `[s,s+n)` do not overlap. Returns: `s`. for each `i` in `[0,n)`, performs `X::assign(s[i],p[i])`. | linear
> >    > `[…]`

> Option B:

> > NAD (i.e. implementations need to be fixed, in practice `char_traits::copy` and `char_traits::move` might be equivalent).

[Kona 2022-11-11; Move to Ready]

LWG voted for Option A (6 for, 0 against, 1 netural)

Proposed resolution:

1. Edit 23.2.2 [char.traits.require], Table 75 — "Character traits requirements" [tab:char.traits.req], as indicated:

   > Table 75 — Character traits requirements [tab:char.traits.req]
   > Expression | Return type | Assertion/note pre/post-condition | Complexity
   > --- | --- | --- | ---
   > `[…]`
   > `X::copy(s,p,n)` | `X::char_type*` | Preconditions: `p` not in `[s,s+n)`The ranges `[p,p+n)` and `[s,s+n)` do not overlap. Returns: `s`. for each `i` in `[0,n)`, performs `X::assign(s[i],p[i])`. | linear
   > `[…]`

### 3664(i). LWG 3392 broke `std::ranges::distance(a, a+3)`

Section: 25.4.4.3 [range.iter.op.distance] Status: Ready Submitter: Arthur O'Dwyer Opened: 2022-01-23 Last modified: 2022-11-10

Priority: 2

View all other issues in [range.iter.op.distance].

View all issues with Ready status.

Discussion:

Consider the use of `std::ranges::distance(first, last)` on a simple C array. This works fine with `std::distance`, but currently does not work with `std::ranges::distance`.

>     // godbolt link
>     #include <ranges>
>     #include <cassert>

>     int main() {
>       int a[] = {1, 2, 3};
>       assert(std::ranges::distance(a, a+3) == 3);
>       assert(std::ranges::distance(a, a) == 0);
>       assert(std::ranges::distance(a+3, a) == -3);
>     }

Before LWG 3392, we had a single iterator-pair overload:

>     template<input_or_output_iterator I, sentinel_for<I> S>
>       constexpr iter_difference_t<I> distance(I first, S last);

which works fine for C pointers. After LWG 3392, we have two iterator-pair overloads:

>     template<input_or_output_iterator I, sentinel_for<I> S>
>       requires (!sized_sentinel_for<S, I>)
>         constexpr iter_difference_t<I> distance(I first, S last);

>     template<input_or_output_iterator I, sized_sentinel_for<I> S>
>       constexpr iter_difference_t<I> distance(const I& first, const S& last);

and unfortunately the one we want — `distance(I first, S last)` — is no longer viable because [with `I=int*`, `S=int*`], we have `sized_sentinel_for<S, I>` and so its constraints aren't satisfied. So we look at the other overload [with `I=int[3]`, `S=int[3]`], but unfortunately its constraints aren't satisfied either, because `int[3]` is not an `input_or_output_iterator`.

[2022-01-30; Reflector poll]

Set priority to 2 after reflector poll.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4901.

> > [Drafting Note: Thanks to Casey Carter. Notice that `sentinel_for<S, I>` already implies and subsumes `input_or_output_iterator<I>`, so that constraint wasn't doing anything; personally I'd prefer to remove it for symmetry (and to save the environment). Otherwise you'll have people asking why one of the `I`'s is constrained and the other isn't.]

> 1. Modify 25.2 [iterator.synopsis], header `<iterator>` synopsis, as indicated:

>    >     […]
>    >     // 25.4.4.3 [range.iter.op.distance], ranges::distance
>    >     template<classinput_or_output_iterator I, sentinel_for<I> S>
>    >       requires (!sized_sentinel_for<S, I>)
>    >       constexpr iter_difference_t<I> distance(I first, S last);
>    >     template<classinput_or_output_iterator I, sized_sentinel_for<decay_t<I>> S>
>    >       constexpr iter_difference_t<I> distance(const I& first, const S& last);
>    >     […]

> 2. Modify 25.4.4.3 [range.iter.op.distance] as indicated:

>    >     template<classinput_or_output_iterator I, sentinel_for<I> S>
>    >       requires (!sized_sentinel_for<S, I>)
>    >       constexpr iter_difference_t<I> ranges::distance(I first, S last);

>    > > -1- Preconditions: `[first, last)` denotes a range.

>    > > -2- Effects: Increments `first` until `last` is reached and returns the number of increments.

>    >     template<classinput_or_output_iterator I, sized_sentinel_for<decay_t<I>> S>
>    >       constexpr iter_difference_t<I> ranges::distance(const I& first, const S& last);

>    > > -3- Effects: Equivalent to: `return last - first;`

[2022-02-16; Arthur and Casey provide improved wording]

[Kona 2022-11-08; Move to Ready]

Proposed resolution:

This wording is relative to N4901.

> [Drafting Note: Arthur thinks it's a bit "cute" of the Effects: element to `static_cast` from `T(&)[N]` to `T* const&` in the array case, but it does seem to do the right thing in all cases, and it saves us from having to use an `if constexpr (is_array_v...)` or something like that.]

1. Modify 25.2 [iterator.synopsis], header `<iterator>` synopsis, as indicated:

   >     […]
   >     // 25.4.4.3 [range.iter.op.distance], ranges::distance
   >     template<classinput_or_output_iterator I, sentinel_for<I> S>
   >       requires (!sized_sentinel_for<S, I>)
   >       constexpr iter_difference_t<I> distance(I first, S last);
   >     template<classinput_or_output_iterator I, sized_sentinel_for<decay_t<I>> S>
   >       constexpr iter_difference_t<decay_t<I>> distance(const I&& first, const S& last);
   >     […]

2. Modify 25.4.4.3 [range.iter.op.distance] as indicated:

   >     template<classinput_or_output_iterator I, sentinel_for<I> S>
   >       requires (!sized_sentinel_for<S, I>)
   >       constexpr iter_difference_t<I> ranges::distance(I first, S last);

   > > -1- Preconditions: `[first, last)` denotes a range.

   > > -2- Effects: Increments `first` until `last` is reached and returns the number of increments.

   >     template<classinput_or_output_iterator I, sized_sentinel_for<decay_t<I>> S>
   >       constexpr iter_difference_t<decay_t<I>> ranges::distance(const I&& first, const S& last);

   > > -3- Effects: Equivalent to: `return last - static_cast<const decay_t<I>&>(first);`

### 3720(i). Restrict the valid types of arg-id for width and precision in std-format-spec

Section: 22.14.2.2 [format.string.std] Status: Ready Submitter: Mark de Wever Opened: 2022-06-19 Last modified: 2022-11-10

Priority: 2

View other active issues in [format.string.std].

View all other issues in [format.string.std].

View all issues with Ready status.

Discussion:

Per 22.14.2.2 [format.string.std]/7

> If `{ arg-idopt }` is used in a width or precision, the value of the corresponding formatting argument is used in its place. If the corresponding formatting argument is not of integral type, or its value is negative for precision or non-positive for width, an exception of type `format_error` is thrown.

The issue is the integral type requirement. The following code is currently valid:

>     std::cout << std::format("{:*^{}}\n", 'a', '0');
>     std::cout << std::format("{:*^{}}\n", 'a', true);

The output of the first example depends on the value of `'0'` in the implementation. When a `char` has `signed char` as underlying type negative values are invalid, while the same value would be valid when the underlying type is `unsigned char`. For the second example the range of a boolean is very small, so this seems not really useful.

Currently libc++ rejects these two examples and MSVC STL accepts them. The members of the MSVC STL team, I spoke, agree these two cases should be rejected.

The following integral types are rejected by both libc++ and MSVC STL:

>     std::cout << std::format("{:*^{}}\n", 'a', L'0');
>     std::cout << std::format("{:*^{}}\n", 'a', u'0');
>     std::cout << std::format("{:*^{}}\n", 'a', U'0');
>     std::cout << std::format("{:*^{}}\n", 'a', u8'0');

In order to accept these character types they need to meet the basic formatter requirements per 22.14.5 [format.functions]/20 and 22.14.5 [format.functions]/25

> `formatter<remove_cvref_t<Ti>, charT>` meets the BasicFormatter requirements (22.14.6.1 [formatter.requirements]) for each `Ti` in `Args`.

which requires adding the following enabled formatter specializations to 22.14.6.3 [format.formatter.spec].

>     template<> struct formatter<wchar_t, char>;

>     template<> struct formatter<char8_t, charT>;
>     template<> struct formatter<char16_t, charT>;
>     template<> struct formatter<char32_t, charT>;

Note, that the specialization `template<> struct formatter<char, wchar_t>` is already required by the Standard.

Not only do they need to be added, but it also needs to be specified how they behave when their value is not in the range of representable values for `charT`.

Instead of requiring these specializations, I propose to go the other direction and limit the allowed types to signed and unsigned integers.

[2022-07-08; Reflector poll]

Set priority to 2 after reflector poll. Tim Song commented:

> "This is technically a breaking change, so we should do it sooner rather than later.

> "I don't agree with the second part of the argument though - I don't see how this wording requires adding those transcoding specializations. Nothing in this wording requires integral types that cannot be packed into basic_format_arg to be accepted.

> "I also think we need to restrict this to signed or unsigned integer types with size no greater than sizeof(long long). Larger types get type-erased into a handle and the value isn't really recoverable without heroics."

Previous resolution [SUPERSEDED]:

> This wording is relative to N4910.

> 1. Modify 22.14.2.2 [format.string.std] as indicated:

>    > -7- If `{ arg-idopt }` is used in a width or precision, the value of the corresponding formatting argument is used in its place. If the corresponding formatting argument is not of integral signed or unsigned integer type, or its value is negative for precision or non-positive for width, an exception of type `format_error` is thrown.

> 2. Add a new paragraph to C.1.8 [diff.cpp20.utilities] as indicated:

>    > Affected subclause: 22.14 [format]

>    > Change: Requirement changes of arg-id of the width and precision fields of std-format-spec. arg-id now requires a signed or unsigned integer type instead of an integral type.

>    > Rationale: Avoid types that are not useful and the need to specify enabled formatter specializations for all character types.

>    > Effect on original feature: Valid C++ 2020 code that passes a boolean or character type as arg-id becomes invalid. For example:

>    > >     std::format("{:*^{}}", "", true); // ill-formed, previously returned "*"

[2022-11-01; Jonathan provides improved wording]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4917.

> 1. Modify 22.14.2.2 [format.string.std] as indicated:

>    > -8- If `{ arg-idopt }` is used in a width or precision, the value of the corresponding formatting argument is used in its place. If the corresponding formatting argument is not of integral standard signed or unsigned integer type, or its value is negative, an exception of type `format_error` is thrown.

> 2. Add a new paragraph to C.1.8 [diff.cpp20.utilities] as indicated:

>    > Affected subclause: 22.14.2.2 [format.string.std]

>    > Change: Restrict types of formatting arguments used as width or precision in a std-format-spec.

>    > Rationale: Avoid types that are not useful or do not have portable semantics.

>    > Effect on original feature: Valid C++ 2020 code that passes a boolean or character type as arg-id becomes invalid. For example:

>    > >     std::format("{:*^{}}", "", true); // ill-formed, previously returned "*"
>    > >     std::format("{:*^{}}", "", '1'); // ill-formed, previously returned an implementation-defined number of '*' characters

[2022-11-10; Jonathan revises wording]

Improve Annex C entry.

[Kona 2022-11-10; Move to Ready]

Proposed resolution:

This wording is relative to N4917.

1. Modify 22.14.2.2 [format.string.std] as indicated:

   > -8- If `{ arg-idopt }` is used in a width or precision, the value of the corresponding formatting argument is used in its place. If the corresponding formatting argument is not of integral standard signed or unsigned integer type, or its value is negative, an exception of type `format_error` is thrown.

2. Add a new paragraph to C.1.8 [diff.cpp20.utilities] as indicated:

   > Affected subclause: 22.14.2.2 [format.string.std]

   > Change: Restrict types of formatting arguments used as width or precision in a std-format-spec.

   > Rationale: Disallow types that do not have useful or portable semantics as a formatting width or precision.

   > Effect on original feature: Valid C++ 2020 code that passes a boolean or character type as arg-id becomes invalid. For example:

   > >     std::format("{:*^{}}", "", true); // ill-formed, previously returned "*"
   > >     std::format("{:*^{}}", "", '1'); // ill-formed, previously returned an implementation-defined number of '*' characters

### 3756(i). Is the `std::atomic_flag` class signal-safe?

Section: 17.14.5 [support.signal], 33.5.10 [atomics.flag] Status: Ready Submitter: Ruslan Baratov Opened: 2022-08-18 Last modified: 2022-11-11

Priority: 3

View all issues with Ready status.

Discussion:

Following document number N4910 about signal-safe instructions 17.14.5 [support.signal] Signal handlers, and it's unclear whether `std::atomic_flag` is signal-safe.

Formally it doesn't fit any of the mentioned conditions:

- `f` is a non-static member function invoked on an object `A`, such that `A.is_lock_free()` yields `true`, or

  > (there is no `is_lock_free` method in `std::atomic_flag` class)

- `f` is a non-member function, and for every pointer-to-atomic argument `A` passed to `f`, `atomic_is_lock_free(A)` yields `true`

  > (`std::atomic_flag` object can't be passed to `atomic_is_lock_free` as argument)

However, `std::atomic_flag` seem to fit well here, it's atomic, and it's always lock-free.

The suggestion is as follows: If `std::atomic_flag` is signal-safe, then it should be explicitly mentioned in 17.14.5 [support.signal], e.g., the following lines should be added:

> - `f` is a non-static member function invoked on an `atomic_flag` object, or

> - `f` is a non-member function, and every pointer-to-atomic argument passed to `f` is `atomic_flag`, or

If the `std::atomic_flag` is not signal-safe, the following note could be added:

> [Note: Even though `atomic_flag` is atomic and lock-free, it's not signal-safe. — end note]

[2022-09-23; Reflector poll]

Set priority to 3 after reflector poll. Send to SG1.

Another way to fix this is to add `is_always_lock_free (=true)` and `is_lock_free() { return true; }` to `atomic_flag`.

[Kona 2022-11-10; SG1 yields a recommendation]

Poll: Adopt the proposed resolution for LWG3756
"f is a non-static member function invoked on an atomic_flag object, or"
"f is a non-member function, and every pointer-to- atomic argument passed to f is atomic_flag, or"

    SF F N A SA
    11 3 0 0 0

Unanimous consent

Previous resolution [SUPERSEDED]:

> This wording is relative to N4917.

> 1. Modify 17.14.5 [support.signal] as indicated:

>    > -1- A call to the function `signal` synchronizes with any resulting invocation of the signal handler so installed.

>    > -2- A plain lock-free atomic operation is an invocation of a function `f` from 33.5 [atomics], such that:

>    > 1. (2.1) — `f` is the function `atomic_is_lock_free()`, or
>    > 2. (2.2) — `f` is the member function `is_lock_free()`, or
>    > 3. (2.?) — `f` is a non-static member function invoked on an `atomic_flag` object, or
>    > 4. (2.?) — `f` is a non-member function, and every pointer-to-atomic argument passed to `f` is `atomic_flag`, or
>    > 5. (2.3) — `f` is a non-static member function invoked on an object `A`, such that `A.is_lock_free()` yields `true`, or
>    > 6. (2.4) — `f` is a non-member function, and for every pointer-to-atomic argument `A` passed to `f`, `atomic_is_lock_free(A)` yields `true`.

>    > -3- An evaluation is signal-safe unless it includes one of the following:

>    > 1. (3.1) — a call to any standard library function, except for plain lock-free atomic operations and functions explicitly identified as signal-safe;

>    >    [Note 1: This implicitly excludes the use of `new` and `delete` expressions that rely on a library-provided memory allocator. — end note]

>    > 2. (3.2) — an access to an object with thread storage duration;
>    > 3. (3.3) — a `dynamic_cast` expression;
>    > 4. (3.4) — throwing of an exception;
>    > 5. (3.5) — control entering a try-block or function-try-block;
>    > 6. (3.6) — initialization of a variable with static storage duration requiring dynamic initialization (6.9.3.3 [basic.start.dynamic], 8.8 [stmt.dcl])206; or
>    > 7. (3.7) — waiting for the completion of the initialization of a variable with static storage duration (8.8 [stmt.dcl]).

>    > A signal handler invocation has undefined behavior if it includes an evaluation that is not signal-safe.

[2022-11-11; Jonathan provides improved wording]

[Kona 2022-11-11; Move to Ready]

Proposed resolution:

This wording is relative to N4917.

1. Modify 17.14.5 [support.signal] as indicated:

   > -1- A call to the function `signal` synchronizes with any resulting invocation of the signal handler so installed.

   > -2- A plain lock-free atomic operation is an invocation of a function `f` from 33.5 [atomics], such that:

   > 1. (2.1) — `f` is the function `atomic_is_lock_free()`, or
   > 2. (2.2) — `f` is the member function `is_lock_free()`, or
   > 3. (2.?) — `f` is a non-static member function of class `atomic_flag`, or
   > 4. (2.?) — `f` is a non-member function, and the first parameter of `f` has type cv `atomic_flag*`, or
   > 5. (2.3) — `f` is a non-static member function invoked on an object `A`, such that `A.is_lock_free()` yields `true`, or
   > 6. (2.4) — `f` is a non-member function, and for every pointer-to-atomic argument `A` passed to `f`, `atomic_is_lock_free(A)` yields `true`.

   > -3- An evaluation is signal-safe unless it includes one of the following:

   > 1. (3.1) — a call to any standard library function, except for plain lock-free atomic operations and functions explicitly identified as signal-safe;

   >    [Note 1: This implicitly excludes the use of `new` and `delete` expressions that rely on a library-provided memory allocator. — end note]

   > 2. (3.2) — an access to an object with thread storage duration;
   > 3. (3.3) — a `dynamic_cast` expression;
   > 4. (3.4) — throwing of an exception;
   > 5. (3.5) — control entering a try-block or function-try-block;
   > 6. (3.6) — initialization of a variable with static storage duration requiring dynamic initialization (6.9.3.3 [basic.start.dynamic], 8.8 [stmt.dcl])206; or
   > 7. (3.7) — waiting for the completion of the initialization of a variable with static storage duration (8.8 [stmt.dcl]).

   > A signal handler invocation has undefined behavior if it includes an evaluation that is not signal-safe.

### 3769(i). `basic_const_iterator::operator==` causes infinite constraint recursion

Section: 25.5.3 [const.iterators] Status: Ready Submitter: Hewill Kang Opened: 2022-09-05 Last modified: 2022-11-10

Priority: 1

View all issues with Ready status.

Discussion:

Currently, `basic_const_iterator::operator==` is defined as a `friend` function:

>     template<sentinel_for<Iterator> S>
>       friend constexpr bool operator==(const basic_const_iterator& x, const S& s);

which only requires `S` to model `sentinel_for<Iterator>`, and since `basic_const_iterator` has a conversion constructor that accepts `I`, this will result in infinite constraint checks when comparing `basic_const_iterator<int*>` with `int*` (online example):

>     #include <iterator>

>     template<std::input_iterator I>
>     struct basic_const_iterator {
>       basic_const_iterator() = default;
>       basic_const_iterator(I);
>       template<std::sentinel_for<I> S>
>       friend bool operator==(const basic_const_iterator&, const S&);
>     };

>     static_assert(std::sentinel_for<basic_const_iterator<int*>, int*>); // infinite meta-recursion

That is, `sentinel_for` ends with `weakly-equality-comparable-with` and instantiates `operator==`, which in turn rechecks `sentinel_for` and instantiates the same `operator==`, making the circle closed.

The proposed resolution is to change `operator==` to be a member function so that `S` is no longer accidentally instantiated as `basic_const_iterator`. The same goes for `basic_const_iterator::operator-`.

[2022-09-23; Reflector poll]

Set priority to 1 after reflector poll.

"Although I am not a big fan of member ==, the proposed solution seems to be simple." "prefer if we would keep `operator==` as non-member for consistency."

Previous resolution from Hewill [SUPERSEDED]:

> This wording is relative to N4917.

> 1. Modify 25.5.3.3 [const.iterators.iterator], class template `basic_const_iterator` synopsis, as indicated:

>    >     namespace std {
>    >       template<class I>
>    >         concept not-a-const-iterator = see below;

>    >       template<input_iterator Iterator>
>    >       class basic_const_iterator {
>    >         Iterator current_ = Iterator();
>    >         using reference = iter_const_reference_t<Iterator>;         // exposition only

>    >       public:
>    >         […]
>    >         template<sentinel_for<Iterator> S>
>    >           friend constexpr bool operator==(const basic_const_iterator& x, const S& s) const;
>    >         […]
>    >         template<sized_sentinel_for<Iterator> S>
>    >           friend constexpr difference_type operator-(const basic_const_iterator& x, const S& y) const;
>    >         template<not-a-const-iteratorsized_sentinel_for<Iterator> S>
>    >           requires sized_sentinel_fordifferent-from<S, Iteratorbasic_const_iterator>
>    >           friend constexpr difference_type operator-(const S& x, const basic_const_iterator& y);
>    >       };
>    >     }

> 2. Modify 25.5.3.5 [const.iterators.ops] as indicated:

>    > […]

>    >       template<sentinel_for<Iterator> S>
>    >         friend constexpr bool operator==(const basic_const_iterator& x, const S& s) const;

>    > > -16- Effects: Equivalent to: `return x.current_ == s;`.

>    > […]

>    >       template<sized_sentinel_for<Iterator> S>
>    >         friend constexpr difference_type operator-(const basic_const_iterator& x, const S& y) const;

>    > > -24- Effects: Equivalent to: `return x.current_ - y;`.

>    >       template<not-a-const-iteratorsized_sentinel_for<Iterator> S>
>    >         requires sized_sentinel_fordifferent-from<S, Iteratorbasic_const_iterator>
>    >         friend constexpr difference_type operator-(const S& x, const basic_const_iterator& y);

>    > > -25- Effects: Equivalent to: `return x - y.current_;`.

[2022-11-04; Tomasz comments and improves proposed wording]

Initially, LWG requested an investigation of alternative resolutions that would avoid using member functions for the affected operators. Later, it was found that in addition to `==`/`-`, all comparison operators (`<`, `>`, `<=`, `>=`, `<=>`) are affected by same problem for the calls with `basic_const_iterator<basic_const_iterator<int*>>` and `int*` as arguments, i.e. `totally_ordered_with<basic_const_iterator<basic_const_iterator<int*>>, int*>` causes infinite recursion in constraint checking.

The new resolution, change all of the friends overloads for operators `==`, `<`, `>`, `<=`, `>=`, `<=>` and `-` that accept `basic_const_iterator` as lhs, to `const` member functions. This change is applied to homogeneous `(basic_const_iterator, basic_const_iterator)` for consistency. For the overload of `<`, `>`, `<=`, `>=` and `-` that accepts `(I, basic_const_iterator)` we declared them as friends and consistently constrain them with `not-const-iterator`. Finally, its put (now member) `operator`<=>`(I)` in the block with other heterogeneous overloads in the synopsis.

The use of member functions addresses issues, because:

- it disallows conversion to `basic_const_iterator` in the left-hand side of op, i.e. eliminates issues for `(sized_)sentinel_for<basic_const_iterator<int*>, int*>` and `totally_ordered<basic_const_iterator<int*>, int*>`
- member functions (in contrast to friends) are not found by ADL, so we do not get multiple candidates for `basic_const_iterator<basic_const_iterator<S>>`, so we address recursion for nested iterators

[Kona 2022-11-08; Move to Ready]

Proposed resolution:

This wording is relative to N4917.

1. Modify 25.5.3.3 [const.iterators.iterator], class template `basic_const_iterator` synopsis, as indicated:

   >     namespace std {
   >       template<class I>
   >         concept not-a-const-iterator = see below;

   >       template<input_iterator Iterator>
   >       class basic_const_iterator {
   >         Iterator current_ = Iterator();
   >         using reference = iter_const_reference_t<Iterator>;         // exposition only

   >       public:
   >         […]
   >         template<sentinel_for<Iterator> S>
   >           friend constexpr bool operator==(const basic_const_iterator& x, const S& s) const;

   >         friend constexpr bool operator<(const basic_const_iterator& x, const basic_const_iterator& y) const
   >           requires random_access_iterator<Iterator>;
   >         friend constexpr bool operator>(const basic_const_iterator& x, const basic_const_iterator& y) const
   >           requires random_access_iterator<Iterator>;
   >         friend constexpr bool operator<=(const basic_const_iterator& x, const basic_const_iterator& y) const
   >           requires random_access_iterator<Iterator>;
   >         friend constexpr bool operator>=(const basic_const_iterator& x, const basic_const_iterator& y) const
   >           requires random_access_iterator<Iterator>;
   >         friend constexpr auto operator<=>(const basic_const_iterator& x, const basic_const_iterator& y) const
   >           requires random_access_iterator<Iterator> && three_way_comparable<Iterator>;

   >         template<different-from<basic_const_iterator> I>
   >         friend constexpr bool operator<(const basic_const_iterator& x, const I& y) const
   >           requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >         template<different-from<basic_const_iterator> I>
   >         friend constexpr bool operator>(const basic_const_iterator& x, const I& y) const
   >           requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >         template<different-from<basic_const_iterator> I>
   >         friend constexpr bool operator<=(const basic_const_iterator& x, const I& y) const
   >           requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >         template<different-from<basic_const_iterator> I>
   >         friend constexpr bool operator>=(const basic_const_iterator& x, const I& y) const
   >           requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >         template<different-from<basic_const_iterator> I>
   >         constexpr auto operator<=>(const I& y) const
   >           requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I> &&
   >        	       three_way_comparable_with<Iterator, I>;
   >         template<not-a-const-iterator I>
   >         friend constexpr bool operator<(const I& y, const basic_const_iterator& x)
   >           requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >         template<not-a-const-iterator I>
   >         friend constexpr bool operator>(const I& y, const basic_const_iterator& x)
   >           requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >         template<not-a-const-iterator I>
   >         friend constexpr bool operator<=(const I& y, const basic_const_iterator& x)
   >           requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >         template<not-a-const-iterator I>
   >         friend constexpr bool operator>=(const I& y, const basic_const_iterator& x)
   >           requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >         template<different-from<basic_const_iterator> I>
   >         friend constexpr auto operator<=>(const basic_const_iterator& x, const I& y)
   >           requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I> &&
   >           	       three_way_comparable_with<Iterator, I>;

   >         […]
   >         template<sized_sentinel_for<Iterator> S>
   >           friend constexpr difference_type operator-(const basic_const_iterator& x, const S& y) const;
   >         template<not-a-const-iteratorsized_sentinel_for<Iterator> S>
   >           requires sized_sentinel_fordifferent-from<S, Iteratorbasic_const_iterator>
   >           friend constexpr difference_type operator-(const S& x, const basic_const_iterator& y);
   >       };
   >     }

2. Modify 25.5.3.5 [const.iterators.ops] as indicated:

   > […]

   >     template<sentinel_for<Iterator> S>
   >       friend constexpr bool operator==(const basic_const_iterator& x, const S& s) const;

   > > -16- Effects: Equivalent to: `return x.current_ == s;`

   >     friend constexpr bool operator<(const basic_const_iterator& x, const basic_const_iterator& y) const
   >       requires random_access_iterator<Iterator>;
   >     friend constexpr bool operator>(const basic_const_iterator& x, const basic_const_iterator& y) const
   >       requires random_access_iterator<Iterator>;
   >     friend constexpr bool operator<=(const basic_const_iterator& x, const basic_const_iterator& y) const
   >       requires random_access_iterator<Iterator>;
   >     friend constexpr bool operator>=(const basic_const_iterator& x, const basic_const_iterator& y) const
   >       requires random_access_iterator<Iterator>;
   >     friend constexpr auto operator<=>(const basic_const_iterator& x, const basic_const_iterator& y) const
   >       requires random_access_iterator<Iterator> && three_way_comparable<Iterator>;

   > > -17- Let op be the operator.

   > > -18- Effects: Equivalent to: `return x.current_ op y.current_;`

   >     template<different-from<basic_const_iterator> I>
   >     friend constexpr bool operator<(const basic_const_iterator& x, const I& y) const
   >       requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >     template<different-from<basic_const_iterator> I>
   >     friend constexpr bool operator>(const basic_const_iterator& x, const I& y) const
   >       requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >     template<different-from<basic_const_iterator> I>
   >     friend constexpr bool operator<=(const basic_const_iterator& x, const I& y) const
   >       requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >     template<different-from<basic_const_iterator> I>
   >     friend constexpr bool operator>=(const basic_const_iterator& x, const I& y) const
   >       requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
   >     template<different-from<basic_const_iterator> I>
   >     friend constexpr auto operator<=>(const basic_const_iterator& x, const I& y) const
   >       requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I> &&
   >       	       three_way_comparable_with<Iterator, I>;

   > > -19- Let `op` be the operator.

   > > -20- Returns Effects: Equivalent to: `return x.current_ op y;`

   > […]

   >     template<sized_sentinel_for<Iterator> S>
   >       friend constexpr difference_type operator-(const basic_const_iterator& x, const S& y) const;

   > > -24- Effects: Equivalent to: `return x.current_ - y;`

   >     template<not-a-const-iteratorsized_sentinel_for<Iterator> S>
   >       requires sized_sentinel_fordifferent-from<S, Iteratorbasic_const_iterator>
   >       friend constexpr difference_type operator-(const S& x, const basic_const_iterator& y);

   > > -25- Effects: Equivalent to: `return x - y.current_;`

### 3807(i). The feature test macro for `ranges::find_last` should be renamed

Section: 17.3.2 [version.syn] Status: Ready Submitter: Daniel Marshall Opened: 2022-11-02 Last modified: 2022-11-12

Priority: Not Prioritized

View other active issues in [version.syn].

View all other issues in [version.syn].

View all issues with Ready status.

Discussion:

The current feature test macro is `__cpp_lib_find_last` which is inconsistent with almost all other ranges algorithms which are in the form `__cpp_lib_ranges_xxx`.

Proposed resolution is to rename the macro to `__cpp_lib_ranges_find_last`.

[Kona 2022-11-12; Move to Ready]

Proposed resolution:

This wording is relative to N4917.

1. Modify 17.3.2 [version.syn], header `<version>` synopsis, as indicated:

   >     […]
   >     #define __cpp_lib_filesystem              201703L // also in <filesystem>
   >     #define __cpp_lib_ranges_find_last        202207L // also in <algorithm>
   >     #define __cpp_lib_flat_map                202207L // also in <flat_map>
   >     […]

### 3811(i). `views::as_const` on `ref_view<T>` should return `ref_view<const T>`

Section: 26.7.21.1 [range.as.const.overview] Status: Ready Submitter: Tomasz Kamiński Opened: 2022-11-03 Last modified: 2022-11-10

Priority: Not Prioritized

View all issues with Ready status.

Discussion:

For `v` being a non-const lvalue of type `std::vector<int>`, `views::as_const(v)` produces `ref_view<std::vector<int> const>`. However, when `v` is converted to `ref_view` by using `views::all`, `views::as_const(views::all(v))` produces `as_const_view<ref_view<std::vector<int>>>`.

Invoking `views::as_const` on `ref_view<T>` should produce `ref_view<const T>` when `const T` models a constant range. This will reduce the number of instantiations, and make a behavior of `views::as_const` consistent on references and `ref_view` to containers.

[Kona 2022-11-08; Move to Ready]

Proposed resolution:

This wording is relative to N4917.

1. Modify 26.7.21.1 [range.as.const.overview] as indicated:

   > [Drafting note: If we have `ref_view<V>`, when `V` is constant propagating view (`single_view`, `owning_view`), we still can (and should) produce `ref_view<V const>`. This wording achieves that.]

   > -2- The name `views::as_const` denotes a range adaptor object (26.7.2 [range.adaptor.object]). Let `E` be an expression, let `T` be `decltype((E))`, and let `U` be `remove_cvref_t<T>`. The expression `views::as_const(E)` is expression-equivalent to:

   > 1. (2.1) — If `views::all_t<T>` models `constant_range`, then `views::all(E)`.

   > 2. (2.2) — Otherwise, if `U` denotes `span<X, Extent>` for some type `X` and some extent `Extent`, then `span<const X, Extent>(E)`.

   > 3. (2.?) — Otherwise, if `U` denotes `ref_view<X>` for some type `X` and `const X` models `constant_range`, then `ref_view(static_cast<const X&>(E.base()))`.

   > 4. (2.3) — Otherwise, if `E` is an lvalue, `const U` models `constant_range`, and `U` does not model `view`, then `ref_view(static_cast<const U&>(E))`.

   > 5. (2.4) — Otherwise, `as_const_view(E)`.

### 3819(i). `reference_meows_from_temporary` should not use `is_meowible`

Section: 21.3.5.4 [meta.unary.prop] Status: Tentatively Ready Submitter: Tim Song Opened: 2022-11-08 Last modified: 2022-11-10

Priority: Not Prioritized

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

Discussion:

The intent of P2255R2 is for the `reference_meows_from_temporary` traits to fully support cases where a prvalue is used as the source. Unfortunately the wording fails to do so because it tries to use the `is_meowible` traits to say "the initialization is well-formed", but those traits only consider initialization from xvalues, not prvalues. For example, given:

>     struct U {
>       U();
>       U(U&&) = delete;
>     };

>     struct T {
>       T(U);
>     };

`reference_constructs_from_temporary_v<const T&, U>` should be true, but is currently defined as `false`. We need to spell out the "is well-formed" condition directly.

[Kona 2022-11-08; Move to Tentatively Ready]

Proposed resolution:

This wording is relative to N4917.

> [Drafting note: The note is already repeated every time we talk about "immediate context".]

1. Modify 21.3.3 [meta.type.synop], Table 46 ([tab:meta.unary.prop]) — "Type property predicates" — as indicated:

   > Table 46: Type property predicates [tab:meta.unary.prop]
   > Template | Condition | Preconditions
   > --- | --- | ---
   > `…`
   > `template<class T, class U> struct reference_constructs_from_temporary;` | `conjunction_v<is_reference<T>, is_constructible<T, U>>` is `true` `T` is a reference type, and the initialization `T t(VAL<U>);` is well-formed and binds `t` to a temporary object whose lifetime is extended (6.7.7 [class.temporary]). Access checking is performed as if in a context unrelated to `T` and `U`. Only the validity of the immediate context of the variable initialization is considered. [Note ?: The initialization can result in effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such effects are not in the "immediate context" and can result in the program being ill-formed. — end note] | `T` and `U` shall be complete types, cv `void`, or arrays of unknown bound.
   > `template<class T, class U> struct reference_converts_from_temporary;` | `conjunction_v<is_reference<T>, is_convertible<U, T>>` is `true` `T` is a reference type, and the initialization `T t = VAL<U>;` is well-formed and binds `t` to a temporary object whose lifetime is extended (6.7.7 [class.temporary]). Access checking is performed as if in a context unrelated to `T` and `U`. Only the validity of the immediate context of the variable initialization is considered. [Note ?: The initialization can result in effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such effects are not in the "immediate context" and can result in the program being ill-formed. — end note] | `T` and `U` shall be complete types, cv `void`, or arrays of unknown bound.

### 3820(i). `cartesian_product_view::iterator::prev` is not quite right

Section: 26.7.31.3 [ranges.cartesian.iterator] Status: Ready Submitter: Hewill Kang Opened: 2022-11-08 Last modified: 2022-11-10

Priority: Not Prioritized

View all other issues in [ranges.cartesian.iterator].

View all issues with Ready status.

Discussion:

Currently, `cartesian_product_view::iterator::prev` has the following Effects:

>     auto& it = std::get<N>(current_);
>     if (it == ranges::begin(std::get<N>(parent_->bases_))) {
>       it = cartesian-common-arg-end(std::get<N>(parent_->bases_));
>       if constexpr (N > 0) {
>         prev<N - 1>();
>       }
>     }
>     --it;

which decrements the underlying iterator one by one using recursion. However, when `N == 0`, it still detects if the first iterator has reached the beginning and assigns it to the end, which is not only unnecessary, but also causes `cartesian-common-arg-end` to be applied to the first range, making it ill-formed in some cases, for example:

>     #include <ranges>

>     int main() {
>       auto r = std::views::cartesian_product(std::views::iota(0));
>       r.begin() += 3; // hard error
>     }

This is because, for the first range, `cartesian_product_view::iterator::operator+=` only requires it to model `random_access_range`. However, when `x` is negative, this function will call `prev` and indirectly calls `cartesian-common-arg-end`, since the latter constrains its argument to satisfy `cartesian-product-common-arg`, that is, `common_range<R> || (sized_range<R> && random_access_range<R>)`, which is not the case for the unbounded `iota_view`, resulting in a hard error in `prev`'s function body.

The proposed resolution changes the position of the `if constexpr` so that we just decrement the first iterator and nothing else.

[Kona 2022-11-08; Move to Ready]

Proposed resolution:

This wording is relative to N4917.

1. Modify 26.7.31.3 [ranges.cartesian.iterator] as indicated:

   >     template<size_t N = sizeof...(Vs)>
   >       constexpr void prev();

   > > -6- Effects: Equivalent to:

   > >     auto& it = std::get<N>(current_);
   > >     if constexpr (N > 0) {
   > >       if (it == ranges::begin(std::get<N>(parent_->bases_))) {
   > >         it = cartesian-common-arg-end(std::get<N>(parent_->bases_));
   > >         if constexpr (N > 0) {
   > >           prev<N - 1>();
   > >         }
   > >       }
   > >     }
   > >     --it;

### 3825(i). Missing compile-time argument `id` check in `basic_format_parse_context::next_arg_id`

Section: 22.14.6.5 [format.parse.ctx] Status: Ready Submitter: Victor Zverovich Opened: 2022-11-09 Last modified: 2022-11-11

Priority: Not Prioritized

View all issues with Ready status.

Discussion:

The definition of `check_arg_id` in 22.14.6.5 [format.parse.ctx] includes a (compile-time) argument `id` check in the Remarks element:

>     constexpr void check_arg_id(size_t id);

> > […]

> > Remarks: Call expressions where `id >= num_args_` are not core constant expressions (7.7 [expr.const]).

However, a similar check is missing from `next_arg_id` which means that there is no argument id validation in user-defined format specification parsing code that invokes this function (e.g. when parsing automatically indexed dynamic width).

Previous resolution [SUPERSEDED]:

> This wording is relative to N4917.

> 1. Modify 22.14.6.5 [format.parse.ctx] as indicated:

>    >     constexpr size_t next_arg_id();

>    > > -7- Effects: If `indexing_ != manual`, equivalent to:

>    > > >     if (indexing_ == unknown)
>    > > >       indexing_ = automatic;
>    > > >     return next_arg_id_++;

>    > > -8- Throws: `format_error` if `indexing_ == manual` which indicates mixing of automatic and manual argument indexing.

>    > > -?- Remarks: Call expressions where `next_arg_id_ >= num_args_` are not core constant expressions (7.7 [expr.const]).

[2022-11-11; Tomasz provide improved wording; Move to Open]

Clarify that the value of `next_arg_id_` is used, and add missing "is `true`."

[Kona 2022-11-11; move to Ready]

Proposed resolution:

This wording is relative to N4917.

1. Modify 22.14.6.5 [format.parse.ctx] as indicated:

   >     constexpr size_t next_arg_id();

   > > -7- Effects: If `indexing_ != manual` is `true`, equivalent to:

   > > >     if (indexing_ == unknown)
   > > >       indexing_ = automatic;
   > > >     return next_arg_id_++;

   > > -8- Throws: `format_error` if `indexing_ == manual` is `true` which indicates mixing of automatic and manual argument indexing.

   > > -?- Remarks: Let cur-arg-id be the value of `next_arg_id_` prior to this call. Call expressions where `cur-arg-id >= num_args_` is `true` are not core constant expressions (7.7 [expr.const]).

   >     constexpr size_t check_arg_id(size_t id);

   > > -9- Effects: If `indexing_ != automatic` is `true`, equivalent to:

   > > >     if (indexing_ == unknown)
   > > >       indexing_ = manual;

   > > -10- Throws: `format_error` if `indexing_ == automatic` is `true` which indicates mixing of automatic and manual argument indexing.

   > > -11- Remarks: Call expressions where `id >= num_args_` is `true` are not core constant expressions (7.7 [expr.const]).