3551. borrowed_{iterator,subrange}_t are overspecified

Section: 25.5.5 [range.dangling] Status: C++23 Submitter: Tim Song Opened: 2021-05-12 Last modified: 2023-11-22

Priority: Not Prioritized

View all issues with C++23 status.

Discussion:

As discussed in P1715R0, there are ways to implement something equivalent to std::conditional_t that are better for compile times. However, borrowed_{iterator,subrange}_t are currently specified to use conditional_t, and this appears to be user-observable due to the transparency of alias templates. We should simply specify the desired result and leave the actual definition to the implementation.

[2021-05-20; Reflector poll]

Set status to Tentatively Ready after seven votes in favour during reflector poll.

[2021-06-07 Approved at June 2021 virtual plenary. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4885.

  1. Modify 25.2 [ranges.syn], header <ranges> synopsis, as indicated:

      […]
    namespace std::ranges {
      […]
      // 25.5.5 [range.dangling], dangling iterator handling
      struct dangling;
      
      template<range R>
        using borrowed_iterator_t = see belowconditional_t<borrowed_range<R>, iterator_t<R>, dangling>;
    
      template<range R>
        using borrowed_subrange_t = see below
          conditional_t<borrowed_range<R>, subrange<iterator_t<R>>, dangling>;
    
      […]
    }
    
  2. Modify 25.5.5 [range.dangling] as indicated:

    -1- The tag type dangling is used together with the template aliases borrowed_iterator_t and borrowed_subrange_t. When an algorithm that typically returns an iterator into, or a subrange of, a range argument is called with an rvalue range argument that does not model borrowed_range (25.4.2 [range.range]), the return value possibly refers to a range whose lifetime has ended. In such cases, the tag type dangling is returned instead of an iterator or subrange.

    namespace std::ranges {
      struct dangling {
        […]
      };
    }
    

    -2- [Example 1: […] — end example]

    -?- For a type R that models range:

    1. (?.1) — if R models borrowed_range, then borrowed_iterator_t<R> denotes iterator_t<R>, and borrowed_subrange_t<R> denotes subrange<iterator_t<R>>;

    2. (?.2) — otherwise, both borrowed_iterator_t<R> and borrowed_subrange_t<R> denote dangling.