4057. generator::iterator's operator* is not noexcept when it can be

Section: 25.8.6 [coro.generator.iterator] Status: New Submitter: S. B. Tam Opened: 2024-03-01 Last modified: 2024-09-17

Priority: 3

View other active issues in [coro.generator.iterator].

View all other issues in [coro.generator.iterator].

View all issues with New status.

Discussion:

generator::iterator's operator* is specified to have the following noexcept-specifier:

noexcept(is_nothrow_copy_constructible_v<reference>)

When reference is an rvalue reference type, is_nothrow_copy_constructible_v<reference> is false (because reference is not copy constructible), which means operator* is not noexcept.

However, operator* doesn't perform any potentially-throwing operation in this case. It's effect is equivalent to return static_cast<reference>(*p.value_); (where the type of p.value_ is effectively add_pointer_t<reference>). Neither the dereference nor the cast to rvalue reference can throw an exception.

I think the expression inside the noexcept-specifier should be changed to noexcept(static_cast<reference>(*p.value_)).

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll. "Just remove the noexcept-specifier." "Maybe add is_reference_v<reference> || ..."

[2024-09-16; Casey provides wording]

In the prioritization thread, LWG discussed a couple of alternatives without much enthusiasm. One of the alternatives was to simply remove the noexcept-specifier, which seems to be the most efficient use of LWG's time.

Proposed resolution:

This wording is relative to N4988.

  1. Modify 25.8.6 [coro.generator.iterator] as indicated:

    namespace std {
      template<class Ref, class Val, class Allocator>
      class generator<Ref, Val, Allocator>::iterator {
        […]
        reference operator*() const noexcept(is_nothrow_copy_constructible_v<reference>);
        […]
      };
    }
    
    […]

    -3- Returns: *this.

    reference operator*() const noexcept(is_nothrow_copy_constructible_v<reference>l);
    

    -4- Preconditions: For some generator object x, coroutine_ is in *x.active_ and x.active_->top() refers to a suspended coroutine with promise object p.