Revised 2022-11-24 at 09:59:48 UTC

## Tentative Issues

### 2116(i). `is_nothrow_constructible` and destructors

Section: 21.3.5.4 [meta.unary.prop] Status: Tentatively NAD Submitter: Dave Abrahams Opened: 2011-12-09 Last modified: 2022-10-10

Priority: 3

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

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

View all issues with Tentatively NAD status.

Discussion:

IMO if we specified `is_[nothrow_]constructible` in terms of a variable declaration whose validity requires destructibility, it is clearly a bug in our specification and a failure to realize the actual original intent. The specification should have been in terms of placement-new.

Daniel:
At the time of the specification this was intended and the solution is not done by removing the destruction semantics of `is_constructible`.

The design of `is_constructible` was also impacted by the previous `Constructible` concept that explicitly contained destruction semantics, because during conceptification of the library it turned out to simplify the constraints in the library because you did not need to add `Destructible` all the time. It often was implied but never spoken out in C++03.

Pure construction semantics was considered as useful as well, so `HasConstructor` did also exist and would surely be useful as trait as well.

Another example that is often overlooked: This also affects wrapper types like `pair`, `tuple`, `array` that contain potentially more than one type: This is easy to understand if you think of `T1` having a deleted destructor and `T2` having a constructor that may throw: Obviously the compiler has potentially need to use the `destructor` of `T1` in the constructor of `std::pair<T1, T2>` to ensure that the core language requirements are satisfied (All previous fully constructed sub-objects must be destructed).

The core language also honors this fact in [class.copy] p11:

> A defaulted copy/move constructor for a class `X` is defined as deleted (9.5.3 [dcl.fct.def.delete]) if `X` has:
> […]
> — any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
> […]

Dave:
This is about `is_nothrow_constructible` in particular. The fact that it is foiled by not having a `noexcept` dtor is a defect.

[2012, Kona]

Move to Open.

`is_nothrow_constructible` is defined in terms of `is_constructible`, which is defined by looking at a hypothetical variable and asking whether the variable definition is known not to throw exceptions. The issue claims that this also examines the type's destructor, given the context, and thus will return `false` if the destructor can potentially throw. At least one implementation (Howard's) does return `false` if the constructor is `noexcept(true)` and the destructor is `noexcept(false)`. So that's not a strained interpretation. The issue is asking for this to be defined in terms of placement `new`, instead of in terms of a temporary object, to make it clearer that `is_nothrow_constructible` looks at the `noexcept` status of only the constructor, and not the destructor.

Sketch of what the wording would look like:

require `is_constructible`, and then also require that a placement `new` operation does not throw. (Remembering the title of this issue... What does this imply for `swap`?

If we accept this resolution, do we need any changes to `swap`?

STL argues: no, because you are already forbidden from passing anything with a throwing destructor to `swap`.

Dietmar argues: no, not true. Maybe statically the destructor can conceivably throw for some values, but maybe there are some values known not to throw. In that case, it's correct to pass those values to `swap`.

[2017-01-27 Telecon]

Gave the issue a better title

This issue interacts with 2827

Ville would like "an evolution group" to take a look at this issue.

[2020-08; LWG reflector]

A poll was taken to close the issue as NAD, but only gained three votes in favour (and one vote against, which was subsequently withdrawn).

[2022-03; LWG reflector]

A poll was taken to close the issue as NAD, with six votes in favour. (and one vote against, subsequently withdrawn).

"Write a paper if you want something else. These traits have well established meaning now." "Minimizing requirements is not as important a concern for standard library concepts as as minimizing the number of concepts. Requirements like 'I need to construct but not destroy an object' are niche enough that we don't need to support them."

Proposed resolution:

### 2432(i). `initializer_list` assignability

Section: 17.11 [support.initlist] Status: Tentatively NAD Submitter: David Krauss Opened: 2014-09-30 Last modified: 2015-05-22

Priority: 2

View other active issues in [support.initlist].

View all other issues in [support.initlist].

View all issues with Tentatively NAD status.

Discussion:

`std::initializer_list::operator=` 17.11 [support.initlist] is horribly broken and it needs deprecation:

>     std::initializer_list<foo> a = {{1}, {2}, {3}};
>     a = {{4}, {5}, {6}};
>     // New sequence is already destroyed.

Assignability of `initializer_list` isn't explicitly specified, but most implementations supply a default assignment operator. I'm not sure what 16.3 [description] says, but it probably doesn't matter.

[Lenexa 2015-05-05: Send to EWG as discussed in Telecon]

[2022-08-24; Reflector poll]

Set status to Tentatively NAD after reflector poll in October 2021.

"If somebody wants to revisit it, they'll need to write a paper to demonstrate what the change would break, whether that would be a problem in practice, and convince the evolution groups to make a change. But it's not an LWG issue."

Proposed resolution:

1. Edit 17.11 [support.initlist] p1, class template `initializer_list` synopsis, as indicated:

   >     namespace std {
   >       template<class E> class initializer_list {
   >       public:
   >         […]
   >         constexpr initializer_list() noexcept;

   >         initializer_list(const initializer_list&) = default;
   >         initializer_list(initializer_list&&) = default;
   >         initializer_list& operator=(const initializer_list&) = delete;
   >         initializer_list& operator=(initializer_list&&) = delete;

   >         constexpr size_t size() const noexcept;
   >         […]
   >       };
   >       […]
   >     }

### 3357(i). [fund.ts.v3] `default_random_engine` is overspecified for per-thread engine

Section: 99 [fund.ts.v3::rand.util.randint] Status: Tentatively NAD Submitter: Zhihao Yuan Opened: 2019-12-10 Last modified: 2022-10-19

Priority: 3

View all issues with Tentatively NAD status.

Discussion:

Addresses: fund.ts.v3

Although "implementation may select this type on the basis of performance, size, quality, or any combination of such factors," but changing this typedef is an ABI-break for implementations. Specifying per-thread engine to use this typedef results in losses of performance, size, and/or quality.

Since this type is not involved in `randint` facilities' interface (other than its member typedef), the current specification should be relaxed.

[2020-01 Priority set to 3 and assigned to LEWG after review on the reflector.]

[2020-05-28; LEWG issue reviewing]

LEWG issue processing voted to reject 3357 as NAD. Status change to Open.

>     Reject LWG3357 as NAD

>     SF  F N A SA
>     1  10 4 2 1

[2022-10-19; Reflector poll]

Set status to "Tentatively NAD" based on LEWG recommendation and reflector poll.

Proposed resolution:

This wording is relative to N4840.

1. Modify 9.1.1 [fund.ts.v3::rand.syn], header `<experimental/random>` synopsis, as indicated:

   >     #include <random>

   >     namespace std::experimental {
   >     inline namespace fundamentals_v3 {

   >       // 10.1.2.1, Function template randint
   >       template <class IntType>
   >       IntType randint(IntType a, IntType b);
   >       void reseed();
   >       void reseed(default_random_engine::result_typeuint_fast32_t value);

   >     } // inline namespace fundamentals_v3
   >     } // namespace std::experimental

2. Modify 99 [fund.ts.v3::rand.util.randint] as indicated:

   > -1- A separate per-thread engine of type `default_random_engine` (C++17 §29.6.5)unspecified type that meets the requirements of random number engine (C++17 [rand.req.eng]), initialized to an unpredictable state, shall be maintained for each thread. [Note: The implementation may choose the engine type on the basis of performance, size, quality, or any combination of such factors, so as to provide at least acceptable engine behavior for relatively casual, inexpert, and/or lightweight use. — end note]

   > […]

   >     void reseed();
   >     void reseed(default_random_engine::result_typeuint_fast32_t value);

   > > -7- Effects: Let `g` be the per-thread engine. The first form sets `g` to an unpredictable state. The second form invokes `g.seed(value)`.

   > > -8- Postconditions: Subsequent calls to `randint` do not depend on values produced by `g` before calling reseed. [Note: `reseed` also resets any instances of `uniform_int_distribution` used by `randint`. — end note]

### 3726(i). `reverse_iterator::operator->` is underconstrained for non-pointer iterators

Section: 25.5.1.6 [reverse.iter.elem] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2022-06-27 Last modified: 2022-08-23

Priority: Not Prioritized

View other active issues in [reverse.iter.elem].

View all other issues in [reverse.iter.elem].

View all issues with Tentatively NAD status.

Discussion:

For non-pointer types, `reverse_iterator::operator->` only requires the expression `i.operator->()` to be well-formed.

Since the return type of this function is explicitly specified as `pointer`, this will cause a hard error in the function body when the return type of `i.operator->()` cannot be converted to `pointer`.

We should add a return type constraint for this.

[2022-08-23; Reflector poll: NAD]

`pointer` is `iterator_traits<Iterator>::pointer`, which is required to name `decltype(i.operator->())` (25.3.2.3 [iterator.traits]/1) so the postulated problem simply does not arise in valid code.

Proposed resolution:

This wording is relative to N4910.

1. Modify 25.5.1.6 [reverse.iter.elem] as indicated:

   >     constexpr pointer operator->() const
   >       requires (is_pointer_v<Iterator> ||
   >                 requires(const Iterator i) { { i.operator->() } -> convertible_to<pointer>; });

   > > -2- Effects:

   > > 1. (2.1) — If `Iterator` is a pointer type, equivalent to: `return prev(current);`

   > > 2. (2.2) — Otherwise, equivalent to: `return prev(current).operator->();`

### 3727(i). `reverse_iterator`/`common_iterator`'s `operator->` should not require the underlying iterator's `operator->` to be a `const` member function

Section: 25.5.1.6 [reverse.iter.elem], 25.5.5.4 [common.iter.access] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2022-06-27 Last modified: 2022-08-23

Priority: Not Prioritized

View other active issues in [reverse.iter.elem].

View all other issues in [reverse.iter.elem].

View all issues with Tentatively NAD status.

Discussion:

For non-pointer types, `reverse_iterator::operator->` requires that the `Iterator` must have an `operator->()` with `const`-qualifier, whereas in the Effects clause, it always invokes the non-`const` object's `operator->()`.

`common_iterator::operator->` also requires that `I::operator->()` must be `const`-qualified, which seems reasonable since the return type of `get<I>(v_)` is `const I&`. However, LWG 3672 makes `common_iterator::operator->()` always return a value, which makes it unnecessary to detect the constness of `I::operator->()`, because it will be invoked with a non-`const` returned object anyway.

I think we should remove this constraint as I don't see the benefit of doing that. Constraining iterator's `operator->()` to be `const` and finally invoking non-`const` overload doesn't feel right to me either. In `<ranges>`, the exposition-only constraint `has-arrow` (26.5.2 [range.utility.helpers]) for `operator->()` does not require that the underlying iterator's `operator->()` to be `const`, we should make them consistent, and I believe this relaxation of constraints can bring some value.

Daniel:

This issue's second part of the resolution actually depends on 3672 being applied. But note that the reference wording below is still N4910.

[2022-08-23; Reflector poll: NAD]

Implicit variations apply to those requires-expressions, so calling as non-const (and rvalue) is fine. The PR actually loses that property and makes those overloads truly underconstrained. Motivation for relaxing it is vague. As for consistency, we should fix has-arrow instead.

Proposed resolution:

This wording is relative to N4910.

1. Modify 25.5.1.6 [reverse.iter.elem] as indicated:

   >     constexpr pointer operator->() const
   >       requires (is_pointer_v<Iterator> ||
   >                 requires(const Iterator i) { i.operator->(); });

   > > -2- Effects:

   > > 1. (2.1) — If `Iterator` is a pointer type, equivalent to: `return prev(current);`

   > > 2. (2.2) — Otherwise, equivalent to: `return prev(current).operator->();`

2. Modify 25.5.5.4 [common.iter.access] as indicated:

   >     constexpr decltype(auto) operator->() const
   >       requires see below;

   > > -3- The expression in the requires-clause is equivalent to:

   > > >       indirectly_readable<const I> &&
   > > >       (requires(const I& i) { i.operator->(); } ||
   > > >       is_reference_v<iter_reference_t<I>> ||
   > > >       constructible_from<iter_value_t<I>, iter_reference_t<I>>)

   > > -4- Preconditions: `holds_alternative<I>(v_)` is `true`.

   > > -5- Effects:

   > > 1. (5.1) — If `I` is a pointer type or if the expression `get<I>(v_).operator->()` is well-formed `requires(I i) { i.operator->(); }` is `true`, equivalent to: `return get<I>(v_);`

   > > 2. (5.2) — Otherwise, if `iter_reference_t<I>` is a reference type, equivalent to:

   > >    >       auto&& tmp = *get<I>(v_);
   > >    >       return addressof(tmp);

   > > 3. (5.3) — Otherwise, equivalent to: `return proxy(*get<I>(v_));` where `proxy` is the exposition-only class:

   > >    >       class proxy {
   > >    >         iter_value_t<I> keep_;
   > >    >         constexpr proxy(iter_reference_t<I>&& x)
   > >    >           : keep_(std::move(x)) {}
   > >    >       public:
   > >    >         constexpr const iter_value_t<I>* operator->() const noexcept {
   > >    >           return addressof(keep_);
   > >    >         }
   > >    >       };

   > > […]

### 3735(i). `views::adjacent<0>` should be prohibited

Section: 26.7.25.1 [range.adjacent.overview], 26.7.26.1 [range.adjacent.transform.overview] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2022-07-13 Last modified: 2022-08-23

Priority: Not Prioritized

View all issues with Tentatively NAD status.

Discussion:

`views::adjacent` is very similar to `views::slide`, except that the window size `N` is given at compile time.

Since the case where `N` is `0` does not make sense for `slide_view`, LWG 3711 and LWG 3712 added preconditions to the constructor and removed the default constructor, respectively.

But for `views::adjacent`, we can still specify `N` to be `0`. According to the description of 26.7.25.1 [range.adjacent.overview], it will return `views::empty<tuple<>>` as in the case of `views::zip` applied to an empty pack. And for `views::adjacent_transform<0>(E, F)`, it will return `views::zip_transform(F)` and eventually return `empty_view` for some type.

This doesn't seem reasonable to me. The reason why `views::zip` can return `views::empty<tuple<>>` is that the parameter pack can indeed be empty, so this still makes some sense. However, there is no meaningful sense for the word "adjacent" when `N` is `0`.

I don't see any observable value in allowing `views::adjacent<0>`, we should disable it for consistency with `views::slide`.

[2022-08-23; Reflector poll: NAD]

`views::zip()` is exactly as meaningful as `views::adjacent<0>(E)` - it's just the edge case.

Proposed resolution:

This wording is relative to N4910.

1. Modify 26.7.25.1 [range.adjacent.overview] as indicated:

   > -2- The name `views::adjacent<N>` denotes a range adaptor object (26.7.2 [range.adaptor.object]). Given a subexpression `E` and a constant expression `N`, the expression `views::adjacent<N>(E)` is expression-equivalent to:

   > 1. (2.1) — `((void)E, auto(views::empty<tuple<>>))`I if `N` is equal to `0`, `views::adjacent<N>(E)` is ill-formed.

   > 2. (2.2) — O otherwise, `adjacent_view<views::all_t<decltype((E))>, N>(E)`.

2. Modify 26.7.26.1 [range.adjacent.transform.overview] as indicated:

   > -2- The name `views::adjacent_transform<N>` denotes a range adaptor object (26.7.2 [range.adaptor.object]). Given subexpressions `E` and `F` and a constant expression `N`:

   > 1. (2.1) — If `N` is equal to `0`, `views::adjacent_transform<N>(E, F)` is ill-formed expression-equivalent to `((void)E, views::zip_transform(F))`, except that the evaluations of `E` and `F` are indeterminately sequenced.

   > 2. (2.2) — Otherwise, the expression `views::adjacent_transform<N>(E, F)` is expression-equivalent to `adjacent_transform_view<views::all_t<decltype((E))>, decay_t<decltype((F))>, N>(E, F)`.

### 3739(i). `chunk_view::size` should preserve the signedness of the size of the underlying range

Section: 26.7.27.2 [range.chunk.view.input], 26.7.27.6 [range.chunk.view.fwd] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2022-07-15 Last modified: 2022-08-23

Priority: Not Prioritized

View all other issues in [range.chunk.view.input].

View all issues with Tentatively NAD status.

Discussion:

Currently, the Effects of `chunk_view::size` simply returns `to-unsigned-like(div-ceil(ranges::distance(base_), n_))`, where `div-ceil` is defined in 26.7.27.2 [range.chunk.view.input] as:

    template<class I>
    constexpr I div-ceil(I num, I denom) { // exposition only
      I r = num / denom;
      if (num % denom)
        ++r;
      return r;
    }

There are two problems here. First, for the const version of `chunk_view::size`, the types of `ranges::distance(base_)` and `n_` are `range_difference_t<const V>` and `range_difference_t<V>` respectively, and the two parameters of `div-ceil` have the same type `I`. Given that the standard does not guarantee that `V` and `const V` must have the same `difference_type`, this makes the `div-ceil`'s template deduction fail when the two are different.

Second, the standard does not guarantee that `ranges::size` must return an unsigned type, but here we use `to-unsigned-like` to unconditionally convert the return type of `chunk_view::size` to an unsigned type, which is inconsistent with the behavior of other range adaptors such as `take_view` and `drop_view`.

We should try to preserve the characteristics of the `range_size_t` of the underlying range as much as possible.

[2022-08-23; Reflector poll: NAD (would need a paper for LEWG)]

The "range_difference_t<const V> and range_difference_t<V> can be both valid but have different types" part is something I expect Mr. Carter's eventual paper to outlaw. The "preserve signedness" part is LEWG.

Proposed resolution:

This wording is relative to N4910.

1. Modify 26.7.27.2 [range.chunk.view.input] as indicated:

   >     constexpr auto size() requires sized_range<V>;
   >     constexpr auto size() const requires sized_range<const V>;

   > > -5- Effects: Equivalent to:

   > >     return to-unsigned-like(div-ceil(ranges::sizedistance(base_), static_cast<decltype(ranges::size(base_))>(n_));

2. Modify 26.7.27.6 [range.chunk.view.fwd] as indicated:

   >     constexpr auto size() requires sized_range<V>;
   >     constexpr auto size() const requires sized_range<const V>;

   > > -3- Effects: Equivalent to:

   > >     return to-unsigned-like(div-ceil(ranges::sizedistance(base_), static_cast<decltype(ranges::size(base_))>(n_));

### 3740(i). `slide_view::size` should preserve the signedness of underlying range's size

Section: 26.7.28.2 [range.slide.view] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2022-07-15 Last modified: 2022-08-23

Priority: Not Prioritized

View all other issues in [range.slide.view].

View all issues with Tentatively NAD status.

Discussion:

Currently, `slide_view::size const` has the following Effects:

    auto sz = ranges::distance(base_) - n_ + 1;
    if (sz > 0) sz = 0;
    return to-unsigned-like(sz);

There are two problems worth noting here. First, as described in LWG 3739, `ranges::distance(base_)` and `n_` may have different types, which makes the actual type of `sz` not deterministic. Also, the return type is unconditionally converted to an unsigned type, even though the underlying range may have a signed size type.

Second, even if `V` has the same `difference_type` as `const V`, there may still be integer promotion issues mentioned by LWG 3730 since we add an integer `1` at the end here.

I think converting `sz` to the size type of the underlying range before returning is the appropriate thing to do.

[2022-08-23; Reflector poll: NAD]

Paper author: "I did consider promotion and decided not to care. The code compiles and conforms to all ranges requirements, and that seems entirely sufficient to me." "Even if we don't outlaw those being different types entirely, people playing those games will still get exactly one unsigned-integer-like type back. It's totally deterministic."

Proposed resolution:

This wording is relative to N4910.

1. Modify 26.7.28.2 [range.slide.view] as indicated:

   >     constexpr auto size() requires sized_range<V>;
   >     constexpr auto size() const requires sized_range<const V>;

   > > -8- Effects: Equivalent to:

   > >     auto sz = ranges::distance(base_) - n_ + 1;
   > >     if (sz < 0) sz = 0;
   > >     return static_cast<decltype(ranges::size(base_))>to-unsigned-like(sz);

### 3741(i). `std::chrono::abs(duration)` is ill-formed with non-reduced periods

Section: 29.5.10 [time.duration.alg] Status: Tentatively NAD Submitter: Charlie Barto Opened: 2022-07-16 Last modified: 2022-08-24

Priority: Not Prioritized

View all issues with Tentatively NAD status.

Discussion:

Currently 29.5.10 [time.duration.alg] specifies `abs(duration)` as:

> Returns: if `d >= d.zero()`, return `d`, otherwise return `-d`.

Because unary minus on durations is defined to return `common_type_t<duration>(-rep_)`, and `common_type_t` for durations is specified to reduce the period, this is ill-formed with durations such as `duration<int, ratio<1000, 1000>>`, or any other type where the numerator and denominator of the period are not coprime.

[2022-08-23; Reflector poll: NAD]

Not ill-formed, implementation should do a conversion. Changing it to return the reduced duration as an improvement would be for LEWG.

Proposed resolution:

This wording is relative to N4910.

1. Modify 29.2 [time.syn], header `<chrono>` synopsis, as indicated:

   >     […]
   >     // 29.5.10 [time.duration.alg], specialized algorithms
   >     template<class Rep, class Period>
   >       constexpr common_type_t<duration<Rep, Period>> abs(duration<Rep, Period> d);
   >     […]

2. Modify 29.5.10 [time.duration.alg] as indicated:

   > [Drafting note: This will cause `abs` to reduce the period before returning it, much like the other arithmetic operators.

   > This is not a breaking change, because code that was using `abs` with a non-reduced period before did not compile. ]

   >     template<class Rep, class Period>
   >       constexpr common_type_t<duration<Rep, Period>> abs(duration<Rep, Period> d);

   > > -1- Constraints: `numeric_limits<Rep>::is_signed` is `true`.

   > > -2- Returns: If `d >= d.zero()`, return `+d`, otherwise return `-d`.

### 3752(i). Should `string::substr` forward the allocator to the newly created string?

Section: 23.4.3.8.3 [string.substr] Status: Tentatively NAD Submitter: Igor Zhukov Opened: 2022-08-10 Last modified: 2022-08-24

Priority: Not Prioritized

View all issues with Tentatively NAD status.

Discussion:

Adrian Vogelsgesang noticed that libcxx's `string::substr` forward the allocator to the newly created string.

Nikolas Klauser found that MSVC STL does the same thing.

While Casey Carter and we all agree that this is a bug and we will fix it, we've "always" been nonconforming here and it's weird though that there was never an issue filed and two implementations have the exact same change.

So we want a clarification from LWG.

[2022-08-24; Reflector poll]

Set status to Tentatively NAD after reflector poll.

"If you want to provide a different allocator, you can use the substring constructor."

"This matches the constructor, whose choice of allocator has been already considered in 2402. Any changes here need a paper."

Proposed resolution:

### 3768(i). `possibly-const-range` is overconstrained

Section: 26.2 [ranges.syn] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2022-09-08 Last modified: 2022-09-23

Priority: Not Prioritized

View other active issues in [ranges.syn].

View all other issues in [ranges.syn].

View all issues with Tentatively NAD status.

Discussion:

Due to the `possibly-const-range` constraint that the template parameter `R` must model `input_range`, this makes the `ranges::cend` that tries using it for meaningful `const` casting never be applied to an `output_range`, even though `const_sentinel` does not require the template parameter `S` to model `input_iterator`.

This is unnecessary, we should relax its constraint.

[2022-09-23; Reflector poll]

Tentatively NAD. "const output ranges don’t seem to be useful."

Proposed resolution:

This wording is relative to N4917.

1. Modify 26.2 [ranges.syn] as indicated:

   >     #include <compare>              // see 17.12.1 [compare.syn]
   >     #include <initializer_list>     // see 17.11.2 [initializer.list.syn]
   >     #include <iterator>             // see 25.2 [iterator.synopsis]

   >     namespace std::ranges {
   >       […]

   >       // 26.7.21 [range.as.const], as const view
   >       template<input_range R>
   >         constexpr auto& possibly-const-range(R& r) {          // exposition only
   >           if constexpr (constant_range<const R> && !constant_range<R>) {
   >             return const_cast<const R&>(r);
   >           } else {
   >             return r;
   >           }
   >         }

   >       […]
   >     }

### 3779(i). `ranges::fold_*` can unintentionally `const_cast` and `reinterpret_cast`

Section: 27.6.18 [alg.fold] Status: Tentatively NAD Submitter: Nicole Mazzuca Opened: 2022-09-15 Last modified: 2022-10-12

Priority: Not Prioritized

View all issues with Tentatively NAD status.

Discussion:

In the Effects element of `ranges::fold_right`, we get the following code:

>     using U = decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>;
>     if (first == last)
>       return U(std::move(init)); // functional-style C cast
>     […]

Given the following function object:

>     struct Second {
>       static char* operator()(const char*, char* rhs) {
>         return rhs;
>       }
>     };

calling `fold_right` as:

>     char* p = fold_right(views::empty<char*>, "Hello", Second{});

initializes `p` with `const_cast<char*>("Hello")`.

The same problem exists in `fold_left_with_iter`, and thus in `fold_left`.

One can get the `reinterpret_cast` behavior by replacing `const char*` with `unsigned long long`.

[2022-10-12; Reflector poll]

Set status to "Tentatively NAD" after reflector poll.

"The example doesn't compile. The accumulator should be be the second param, but with that fixed the constraints are not satisfied. The `convertible_to` constraint prevents the undesirable casting."

Proposed resolution:

This wording is relative to N4917.

1. Modify 27.6.18 [alg.fold] as indicated:

   >     template<bidirectional_iterator I, sentinel_for<I> S, class T,
   >              indirectly-binary-right-foldable<T, I> F>
   >       constexpr auto ranges::fold_right(I first, S last, T init, F f);
   >     template<bidirectional_range R, class T,
   >              indirectly-binary-right-foldable<T, iterator_t<R>> F>
   >       constexpr auto ranges::fold_right(R&& r, T init, F f);

   > > -3- Effects: Equivalent to:

   > > >     using U = decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>;
   > > >     if (first == last)
   > > >       return static_cast<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<input_iterator I, sentinel_for<I> S, class T,
   >              indirectly-binary-left-foldable<T, I> F>
   >       constexpr see below ranges::fold_left_with_iter(I first, S last, T init, F f);
   >     template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
   >       constexpr see below ranges::fold_left_with_iter(R&& r, T init, F f);

   > > -6- Let `U` be `decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>`.

   > > -7- Effects: Equivalent to:

   > > >     if (first == last)
   > > >       return {std::move(first), static_cast<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)};

### 3789(i). Precondition of (not replaced) `operator delete[]`

Section: 17.7.3.3 [new.delete.array] Status: Tentatively NAD Submitter: blacktea hamburger Opened: 2022-09-25 Last modified: 2022-10-12

Priority: Not Prioritized

View all other issues in [new.delete.array].

View all issues with Tentatively NAD status.

Discussion:

Consider (`operator delete[](std::size_t)` and `operator new(std::size_t)` is not replaced):

>     operator delete[](operator new(1));

(even not replaced) `void* operator new(std::size_t)` does not return `void* operator new[](std::size_t)`. So the behavior is undefined according to 17.7.3.3 [new.delete.array] paragraph 9:

> Preconditions: `ptr` is a null pointer or its value represents the address of a block of memory allocated by an earlier call to a (possibly replaced) `operator new[](std::size_t)` or `operator new[](std::size_t, std::align_val_t)` which has not been invalidated by an intervening call to `operator delete[]`.

However, consider (`operator delete(std::size_t)` and `operator new[](std::size_t)` is not replaced):

>     operator delete(operator new[](1));

(not replaced) `operator new[](std::size_t)` simply returns `operator new(std::size_t)` according to 17.7.3.3 [new.delete.array] paragraph 4:

> Default behavior: Returns `operator new(size)`, or `operator new(size, alignment)`, respectively.

So it is well-formed according to 17.7.3.2 [new.delete.single] paragraph 10:

> Preconditions: `ptr` is a null pointer or its value represents the address of a block of memory allocated by an earlier call to a (possibly replaced) `operator new(std::size_t)` or `operator new(std::size_t, std::align_val_t)` which has not been invalidated by an intervening call to `operator delete`.

The behavior should be consistent.

[2022-10-10; Reflector poll]

Set status to "Tentatively NAD" after reflector poll.

"No reason to carve out an exception covering a case on something which can’t be observed by the program (whether the allocation operators are replaced). This just makes things more complicated for no good reason." "This would require changes to sanitizers and other dynamic analyzers, for zero practical benefit (except allowing bad code to go un-diagnosed)."

Proposed resolution:

This wording is relative to N4917.

1. Modify 17.7.3.3 [new.delete.array] as indicated:

   >     void operator delete[](void* ptr) noexcept;
   >     void operator delete[](void* ptr, std::size_t size) noexcept;
   >     void operator delete[](void* ptr, std::align_val_t alignment) noexcept;
   >     void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept;

   > > -9- Preconditions: `ptr` is a null pointer or its value represents the address of a block of memory allocated by an earlier call to a (possibly replaced) `operator new[](std::size_t)`, or `operator new[](std::size_t, std::align_val_t)`, (not replaced) `operator new(std::size_t)`, or `operator new(std::size_t, std::align_val_t)` which has not been invalidated by an intervening call to `operator delete[]`.

   > > […]

### 3800(i). No deduction guide for `std::match_results`

Section: 32.9.1 [re.results.general] Status: Tentatively NAD Submitter: Jonathan Wakely Opened: 2022-10-25 Last modified: 2022-11-01

Priority: Not Prioritized

View all issues with Tentatively NAD status.

Discussion:

`std::match_results` meets some of the requirements for an allocator-aware container, which means that its `allocator_type` must have the same `value_type` as the container. This means that class template argument deduction should work for:

>     std::match_results mr(alloc);

The allocator's `value_type` will be the `sub_match<Iter>` type stored in the `match_results` object, and so the first template argument for the `match_results` type will be `value_type::iterator`.

P0433R2 added a deduction guide for `std::basic_regex`, but I see no rationale for not adding one for `std::match_results`. This seems like a defect, because all other allocator-aware containers support deduction from an allocator argument.

[2022-11-01; reflector poll]

Status changed to Tentatively NAD. The issue is wrong: other containers do not support deduction from an allocator type.

Proposed resolution:

This wording is relative to N4917.

1. Modify 32.9.1 [re.results.general], class template `match_results` synopsis, as indicated:

   >     namespace std {
   >       template<class BidirectionalIterator,
   >                class Allocator = allocator<sub_match<BidirectionalIterator>>>
   >         class match_results {
   >           […]
   >           void swap(match_results& that);
   >         };

   >       template<class Allocator>
   >         match_results(Allocator) -> match_results<typename Allocator::value_type::iterator, Allocator>;
   >     }

### 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.