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-formedrequires(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<>>))Iif N is equal to 0, views::adjacent<N>(E) is ill-formed.

    2. (2.2) — Ootherwise, 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-formedexpression-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 trueT 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 trueT 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.