<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title><tt>empty_checkable_range</tt></title>
  <style type="text/css">
    body {
      font-variant-ligatures: none;
    }

    p {
      text-align: justify
    }

    li {
      text-align: justify
    }

    blockquote.note,
    div.note {
      background-color: #E0E0E0;
      padding-left: 15px;
      padding-right: 15px;
      padding-top: 1px;
      padding-bottom: 1px;
    }

    p code {
      color: navy
    }

    ins p code {
      color: #00A000
    }

    p ins code {
      color: #00A000
    }

    p del code {
      color: #A00000
    }

    ins {
      color: #00A000
    }

    del {
      color: #A00000
    }

    table#boilerplate {
      border: 0
    }

    table#boilerplate td {
      padding-left: 2em
    }

    table.bordered,
    table.bordered th,
    table.bordered td {
      border: 1px solid;
      text-align: center;
    }

    ins.block {
      color: #00A000;
      text-decoration: none
    }

    del.block {
      color: #A00000;
      text-decoration: none
    }

    #hidedel:checked~* del,
    #hidedel:checked~* del * {
      display: none;
      visibility: hidden
    }
  </style>
</head>

<body data-new-gr-c-s-check-loaded="14.1043.0" data-gr-ext-installed="">
  <table id="boilerplate">
    <tbody>
      <tr>
        <td>Document number</td>
        <td>P3156</td>
      </tr>
      <tr>
        <td>Date</td>
        <td>2024-02-15</td>
      </tr>
      <tr>
        <td>Audience</td>
        <td>LEWG, SG9 (Ranges)
        </td>
      </tr>
      <tr>
        <td>Reply-to</td>
        <td>Hewill Kang &lt;hewillk@gmail.com&gt;</td>
      </tr>
    </tbody>
  </table>
  <hr>
  <h1><tt>empty_checkable_range</tt></h1>
  <ul>
    <li>
      <ul>
        <li>Abstract</li>
        <li>Revision history</li>
        <li>Discussion</li>
        <li>Proposed change</li>
      </ul>
    </li>
  </ul>
  <a name="Abstract"></a>
  <h2>Abstract</h2>
  <p>This paper proposes a new concept <code>empty_checkable_range</code> that is orthogonal to <code>sized_range</code>, to ensure that a range can be checked for emptyness in constant time.
    In addition, <i>input</i> range adaptors 
    have also been enhanced to have an <code>empty()</code> member (if possible).
    </p>
  <h2>Revision history</h2>
  <p>
  <h3>R0</h3>
  <p>Initial revision.</p>
  </p>
  <a name="Discussion"></a>
  <h2>Discussion</h2>
  <p>
  <h3>What's the issue?</h3>
  <p>
    Currently, except for <code>ref_view</code> and <code>owning_view</code>, which explicitly provide the constrained <code>empty()</code> member, other range adaptors do not provide <code>empty()</code>.
    This may be because the original design expected these range adaptors to get free <code>empty()</code> from the <code>view_interface</code>.
  </p>
 <p>However, <code>view_interface::empty()</code> requires derived classes to satisfy <code>forward_range</code> or <code>sized_range</code>, 
  which ultimately results in non-sized input ranges with an valid <code>empty()</code> member losing the functionality to query empty when applied to these adaptors. A real-life example <a href="https://godbolt.org/z/6aW8Gjjj8">would be</a>: </p>
  </p>
  <pre>
      std::istringstream ints("1 2 3 4 5");
      auto s = std::ranges::subrange(std::istream_iterator&lt;int&gt;(ints),
                                     std::istream_iterator&lt;int&gt;());
      std::println("{}", s.empty());
      auto r = s | std::views::transform([](int i) { return i * i; })
      std::println("{}", r.empty()); // <span style="color:red;font-weight:bolder">failed</span>
</pre>
<p>
  <code>views::transform</code> does not change the number of elements of the underlying range, so there is no reason not to preserve the original range's ability to check for emptiness.
  The author believes that for most other adaptors that support input ranges, such as <code>views::as_rvalue</code> or <code>views::enumerate</code>, etc., this can be done by checking if <code>ranges::empty(<i>base_</i>)</code> is valid.
</p>
  <p>
    Another point worth noting is that the current standard does not specify the meaning of <code>ranges::empty</code> nor its time complexity in the same way that <code>sized_range</code> does for <code>ranges::size</code>.
    This makes although <code>ranges::empty(r)</code> appears to be valid, the value returned is not required to be the number of elements in range. 
    For example <code>r.empty()</code> might be similar to <code>vector::clear()</code> for the purpose of clearing the elements with some custom range type.
  </p>
  <p>
    This implies that it is necessary to introduce the concept corresponding to <code>ranges::empty</code> to specify its semantics when applied to ranges.
  </p>
  <p>
    <h3>The <code>empty_checkable_range</code> concept</h3>
<p>
  Currently, the <code>empty()</code> of <code>ref_view</code> and <code>owning_view</code> have the following similar definitions:
</p>
<pre>
    constexpr bool empty() requires requires { ranges::empty(<i>r_</i>); }
    { return ranges::empty(<i>r_</i>); }
    constexpr bool empty() const requires requires { ranges::empty(<i>r_</i>); }
    { return ranges::empty(<i>r_</i>); }
</pre>
<p>
  In other words, we only require that <code>ranges::empty</code> can act on the member, 
  but we do not explain the meaning of its return value, nor do we even require its time complexity. 
  Even <code>const R</code> may not be a <code>range</code>.
</p>
    <p>
      In order to clearly specify semantic requirements of <code>ranges::empty</code> in the ranges world, this paper introduces a new concept named <code>empty_checkable_range</code> similar to <code>sized_range</code>, which basically has the following definition:
    </p>
    <pre>
    template&lt;class T&gt;
    concept empty_checkable_range = range&lt;T&gt; && requires(T&amp; t) { ranges::empty(t); };</pre>
<p>and accompanied by time complexity requirements. With the help of the new concept, 
      we can respell these empty members as:</p>
      <pre>
    constexpr bool empty() requires empty_checkable_range&lt;R&gt;
    { return ranges::empty(<i>r_</i>); }
    constexpr bool empty() const requires empty_checkable_range&lt;const R&gt;
    { return ranges::empty(<i>r_</i>); }</pre>
    <p>
      If the user does not want to check the emptiness through the <code>empty()</code> member, they can bypass the call by 
      specializing <code>disable_empty_checkable_range</code> and check in other meaningful ways,
      just like <code>disable_sized_range</code> does.</p>
      <h3>Replace existing constraints with <code>empty_checkable_range</code></h3>
  <p>The new <code>empty_checkable_range</code> now can be used to replace members that currently only constrain 
  <code>ranges::empty</code> to well-formed to improve semantics, 
    such as <code>view_interface::operator bool()</code> as well as <code>ref_view::empty()</code> and <code>owning_view::empty()</code>.

  </p>
      <h3>Provides the constrained <code>empty()</code> for the non-forward range adaptors</h3>
  Since <code>view_interface::empty()</code> can always be effectively synthesized by <code>ranges::begin(<i>derived</i>()) == ranges::end(<i>derived</i>())</code> when the derived class satisfies 
  <code>forward_range</code>, this paper only provides <code>empty()</code> for <i>input</i> range adaptors.
<p>
  It should be noted that there are two places where <code>ranges::empty</code> is applied to the forward range to check whether it is empty,
   namely <code>split_view::<i>find-next</i>()</code> and <code>cartesian_product_view::end()</code>.
   Although <code>ranges::empty</code> is always a valid expression, the semantics of <code>r.empty()</code>
   are not required in those cases, the paper prefers to use the formula of  <code>ranges::begin(r) == ranges::end(r)</code> to perform the check.
</p>


      <h4>Adaptors that do not change the number of elements</h4>

<p>
  For adaptors that do not change the number of elements, the <code>empty()</code> member can be provided when the underlying range models 
  <code>empty_checkable_range</code>, by simply returning <code>ranges::empty(<i>base_</i>)</code>.
  <p>This category includes <code>as_rvalue_view</code>, <code>transform_view</code>, <code>common_view</code>, <code>as_const_view</code>, <code>elements_view</code> and <code>enumerate_view</code>.</p>
</p>

      <h4>Adatoprs that will change the number of elements</h4>
      <p>
        There are four input adaptors that change the number of elements and be capable of providing <code>empty()</code> worth discussing, namely
        
        <code>take_view</code>, <code>lazy_split_view</code>, <code>chunk_view</code> and <code>stride_view</code>.</p>
        <ol>
        <li>The formula for <code>empty()</code> member of <code>take_view</code> is relatively trivial: as long as the underlying range is empty, it is always empty; otherwise it is empty when <code>n</code> is <code>0</code>.</li>
        <li><code>lazy_split_view</code> is only when base and pattern range are empty after LWG <a href="https://cplusplus.github.io/LWG/issue4017">4017</a> (if accepted).</li>
        <li>Since the latter two have <i>Preconditions</i> of <code>n &gt; 0</code>, they will only be empty when the underlying range is empty.</li></ol>
        <h4>Adatoprs that containing multiple underlying ranges</h4>
      <p>
For input adaptors that contain multiple ranges such as <code>zip_view</code> and <code>cartesian_product_view</code>, 
the adaptors will be empty when one of the underlying ranges is empty, so its <code>empty()</code> can simply return a logical <i>OR</i> of <code>ranges::empty</code> applies to all underlying ranges.
By analogy, the formula for C++26 <code>concat_view::empty()</code> can be spelled as logical <i>ALL</i> for all the <code>ranges::empty</code> values.
</p>
<p>It should be noted that although <code>cartesian_product_view</code> except the first range, the rest ranges must model <code>forward_range</code>, this means
  that they can all be used to check whether there are no elements by comparing the return values of <code>begin()</code> and <code>end()</code>.
  However, this is no more efficient than calling its <code>empty()</code> member directly, so the author prefers to 
  further constrain these forward ranges to <code>empty_checkable_range</code> in favor of checking through the <code>ranges::empty</code>.</p>
<h3>Do we need to relax the constraints of <code>view_interface::empty()</code>?</h3>
<p>
  Since the paper provides all input range adaptors with an <code>empty()</code> member that constrains the underlying range to <code>empty_checkable_range</code>, 
  this makes <code>view_interface::empty()</code> constraining the derived class to <code>sized_range</code> no longer seems necessary.
  This is because <code>ranges::empty</code> checks <code>ranges::size(r) == 0</code> when <code>r.empty()</code> is not a valid expression, so this is already compatible with all input-sized range cases.
</p>
<p>
  However, the author believes that checking whether the range is empty through <code>ranges::size</code> is still more efficient than constructing iterator-sentinel pair through <code>ranges::begin</code> and <code>ranges::end</code>. 
  
  For this reason, the author does not intend to touch the existing signature of <code>view_interface::empty()</code>.</p>
  
  
  <a name="Proposed-change"></a>
  <h2>Proposed change</h2>
  <p>
    This wording is relative to <a href="https://wg21.link/N4971" title=" Working Draft, Programming Languages — C++">N4971</a>.
  </p>
  </blockquote>
  <ol>
    <li>
      <p>Modify 26.2 <a href="https://eel.is/c++draft/ranges.syn">[ranges.syn]</a>, Header <tt>&lt;ranges&gt;</tt> synopsis, as indicated:</p>

      <blockquote>
        <pre>
#include &lt;compare&gt;              // <i>see <a href="https://eel.is/c++draft/compare.syn">[compare.syn]</a></i>
#include &lt;initializer_list&gt;     // <i>see <a href="https://eel.is/c++draft/initializer.list.syn">[initializer.list.syn]</a></i>
#include &lt;iterator&gt;             // <i>see <a href="https://eel.is/c++draft/iterator.synopsis">[iterator.synopsis]</a></i>

namespace std::ranges {
  [&hellip;]
  // <i><a href="https://eel.is/c++draft/range.sized">[range.sized]</a>, sized ranges</i>
  template&lt;class&gt;
    constexpr bool disable_sized_range = false;                                     // <i>freestanding</i>

  template&lt;class T&gt;
    concept sized_range = <i>see below</i>;                                                // <i>freestanding</i>

  <ins>
  // <i>[range.empty.checkable]</a>, empty checkable ranges</i>
  template&lt;class&gt;
    constexpr bool disable_empty_checkable_range = false;                           // <i>freestanding</i>

  template&lt;class T&gt;
    concept empty_checkable_range = <i>see below</i>;                                      // <i>freestanding</i></ins>
  [&hellip;]
}
    </pre>
      </blockquote>
      <li>
        <p>Modify 26.3.12 <a href="https://eel.is/c++draft/range.prim.empty">[range.prim.empty]</a> as indicated:</p>
          
<p><blockquote>
    -1- The name <tt>ranges::empty</tt> denotes a customization point object (<a href="https://eel.is/c++draft/customization.point.object">[customization.point.object]</a>).
</p>  
<p>
  -2- Given a subexpression <tt>E</tt> with type <tt>T</tt>, let <tt>t</tt> be an lvalue that denotes the reified object for <tt>E</tt>.
  Then: 
  </p>
  <ol style="list-style-type: none">
    <li><p>(2.1) &mdash; If <tt>T</tt> is an array of unknown bound (<a href="https://eel.is/c++draft/dcl.array">[dcl.array]</a>), <tt>ranges::empty(E)</tt> is ill-formed.
    <li><p>(2.2) &mdash; Otherwise, <ins>if <tt>disable_empty_checkable_range&lt;remove_cv_t&lt;T&gt;&gt;</tt> ([range.empty.checkable]) is <tt>false</tt> and </ins><tt>bool(t.empty())</tt> is a valid expression, <tt>ranges::empty(E)</tt> is expression-equivalent to <tt>bool(t.empty())</tt>.
    </p><p>
      [&hellip;]
    </p></blockquote>
      
      </li>
    </li>
    <li>
      <p>Add <b>Empty checkable ranges</b> [range.empty.checkable] after 26.4.3 <a href="https://eel.is/c++draft/range.sized">[range.sized]</a> as indicated:</p>
        <blockquote>
          <p>
          -1- The <tt>empty_checkable_range</tt> concept refines <tt>range</tt> with the requirement that 
          <tt>ranges::empty</tt> can be used to check that there are no elements in the range in amortized constant time.
          </p>
<pre>
template&lt;class T&gt;
  concept empty_checkable_range =
    range&lt;T&gt; && requires(T&amp; t) { ranges::empty(t); };
</pre>
<p>
  -2- Given an lvalue <tt>t</tt> of type <tt>remove_reference_t&lt;T&gt;</tt>, <tt>T</tt> models <tt>empty_checkable_range</tt> only if 
  </p>
  <ol style="list-style-type: none">
    <li><p>(2.1) &mdash; <tt>ranges::empty(t)</tt> is amortized &#x1d4aa;(1), does not modify <tt>t</tt>, and is equal to <tt>ranges::distance(ranges::begin(t), ranges::end(t)) == 0</tt>, and
    <li><p>(2.2) &mdash; if <tt>iterator_t&lt;T&gt;</tt> models <tt>forward_iterator</tt>, <tt>ranges::empty(t)</tt> is well-defined regardless of the evaluation of <tt>ranges::begin(t)</tt>.
    </p><p>
      [<i>Note 1</i>: <tt>ranges::empty(t)</tt> is otherwise not required to be well-defined after evaluating <tt>ranges::begin(t)</tt>.
For example, it is possible for <tt>ranges::empty(t)</tt> to be well-defined for an <tt>empty_checkable_range</tt> whose iterator type does not model <tt>forward_iterator</tt> only if evaluated before the first call to <tt>ranges::begin(t)</tt>.
— <i>end note</i>]
    </p>
    
  </li>
    </ol>
          </blockquote>
        </li>
        <li>
          <p>Modify 26.5.3.1 <a href="https://eel.is/c++draft/view.interface.general">[view.interface.general]</a>, class template
            <tt>view_interface</tt> synopsis, as indicated:</p>
    
          <blockquote>
            <pre>
namespace std::ranges {
  template&lt;D&gt;
    requires is_class_v&lt;D&gt; && same_as&lt;D, remove_cv_t&lt;D&gt;&gt;
  class view_interface {
    [&hellip;]
  
    constexpr explicit operator bool()
      requires <ins>empty_checkable_range&lt;D&gt;</ins><del>requires { ranges::empty(derived()); }</del> {
        return !ranges::empty(<i>derived</i>());
      }
    constexpr explicit operator bool() const
      requires <ins>empty_checkable_range&lt;const D&gt;</ins><del>requires { ranges::empty(derived()); }</del> {
        return !ranges::empty(<i>derived</i>());
      }

    [&hellip;]
  };
  [&hellip;]
}
        </pre>
          </blockquote>
        </li>
        <li>
          <p>Modify 26.7.6.2 <a href="https://eel.is/c++draft/range.ref.view">[range.ref.view]</a>, class template
            <tt>ref_view</tt> synopsis, as indicated:</p>
    
          <blockquote>
            <pre>
namespace std::ranges {
  template&lt;range R&gt;
    requires is_object_v&lt;R&gt;
  class ref_view : public view_interface&lt;ref_view&lt;R&gt;&gt; {
    [&hellip;]

    constexpr bool empty() const
      requires <ins>empty_checkable_range&lt;R&gt;</ins><del>requires { ranges::empty(*<i>r_</i>); }</del>
    { return ranges::empty(*<i>r_</i>); }

    [&hellip;]
  };
  [&hellip;]
}
          </pre>
          </blockquote>
        </li>
        <li>
          <p>Modify 26.7.6.3 <a href="https://eel.is/c++draft/range.owning.view">[range.owning.view]</a>, class template
            <tt>owning_view</tt> synopsis, as indicated:</p>
    
          <blockquote>
            <pre>
namespace std::ranges {
  template&lt;range R&gt;
    requires movable&lt;R&gt; &amp;&amp; (!<i>is-initializer-list</i>&lt;R&gt;) // <i>see <a href="https://eel.is/c++draft/range.refinements">[range.refinements]</a></i>
  class owning_view : public view_interface&lt;owning_view&lt;R&gt;&gt; {
    [&hellip;]

    constexpr bool empty() requires <ins>empty_checkable_range&lt;R&gt;</ins><del>requires { ranges::empty(<i>r_</i>); }</del>
    { return ranges::empty(<i>r_</i>); }
    constexpr bool empty() const requires <ins>empty_checkable_range&lt;const R&gt;</ins><del>requires { ranges::empty(<i>r_</i>); }</del>
    { return ranges::empty(<i>r_</i>); }

    [&hellip;]
  };
  [&hellip;]
}
          </pre>
          </blockquote>
        </li>
    <li>
      <p>Modify 26.7.7.2 <a href="https://eel.is/c++draft/range.as.rvalue.view">[range.as.rvalue.view]</a>, class template
        <tt>as_rvalue_view</tt> synopsis, as indicated:</p>

      <blockquote>
        <pre>
namespace std::ranges {
  template&lt;view V&gt;
    requires input_range&lt;V&gt;
  class as_rvalue_view : public view_interface&lt;as_rvalue_view&lt;V&gt;&gt; {
    [&hellip;]
  
    <ins>constexpr bool empty() requires empty_checkable_range&lt;V&gt;
    { return ranges::empty(<i>base_</i>); }
    constexpr bool empty() const requires empty_checkable_range&lt;const V&gt;
    { return ranges::empty(<i>base_</i>); }</ins>

    constexpr auto size() requires sized_range&lt;V&gt; { return ranges::size(<i>base_</i>); }
    constexpr auto size() const requires sized_range&lt;const V&gt; { return ranges::size(<i>base_</i>); }
  };
  [&hellip;]
}
    </pre>
      </blockquote>
    </li>
    <li>
      <p>Modify 26.7.9.2 <a href="https://eel.is/c++draft/range.transform.view">[range.transform.view]</a>, class template
        <tt>transform_view</tt> synopsis, as indicated:</p>

      <blockquote>
        <pre>
namespace std::ranges {
  template&lt;input_range V, move_constructible F&gt;
    requires view&lt;V&gt; &amp;&amp; is_object_v&lt;F&gt; &amp;&amp;
             regular_invocable&lt;F&amp;, range_reference_t&lt;V&gt;&gt; &amp;&amp;
             <i>can-reference</i>&lt;invoke_result_t&lt;F&amp;, range_reference_t&lt;V&gt;&gt;&gt;
  class transform_view : public view_interface&lt;transform_view&lt;V, F&gt;&gt; {
    [&hellip;]
  
    <ins>constexpr bool empty() requires empty_checkable_range&lt;V&gt;
    { return ranges::empty(<i>base_</i>); }
    constexpr bool empty() const requires empty_checkable_range&lt;const V&gt;
    { return ranges::empty(<i>base_</i>); }</ins>

    constexpr auto size() requires sized_range&lt;V&gt; { return ranges::size(<i>base_</i>); }
    constexpr auto size() const requires sized_range&lt;const V&gt;
    { return ranges::size(<i>base_</i>); }
  };
  [&hellip;]
}
      </pre>
      </blockquote>
    </li>
    <li>
      <p>Modify 26.7.10.2 <a href="https://eel.is/c++draft/range.take.view">[range.take.view]</a>, class template
        <tt>take_view</tt> synopsis, as indicated:</p>

      <blockquote>
        <pre>
namespace std::ranges {
  template&lt;view V&gt;
  class take_view : public view_interface&lt;take_view&lt;V&gt;&gt; {
    [&hellip;]
  
    <ins>constexpr bool empty() requires empty_checkable_range&lt;V&gt; {
      return ranges::empty(<i>base_</i>) || <i>count_</i> == 0;
    }
    constexpr bool empty() const requires empty_checkable_range&lt;const V&gt; {
      return ranges::empty(<i>base_</i>) || <i>count_</i> == 0;
    }</ins>

    constexpr auto size() requires sized_range&lt;V&gt; {
      auto n = ranges::size(<i>base_</i>);
      return ranges::min(n, static_cast&lt;decltype(n)&gt;(<i>count_</i>));
    }
    constexpr auto size() const requires sized_range&lt;const V&gt; {
      auto n = ranges::size(<i>base_</i>);
      return ranges::min(n, static_cast&lt;decltype(n)&gt;(<i>count_</i>));
    }
  };
  [&hellip;]
}
        </pre>
      </blockquote>
    </li>
    <li>
      <p>Modify 26.7.16.2 <a href="https://eel.is/c++draft/range.lazy.split.view">[range.lazy.split.view]</a>, class template
        <tt>lazy_split_view</tt> synopsis, as indicated:</p>

      <blockquote>
        <pre>
namespace std::ranges {
  [&hellip;]
  template&lt;input_range V, forward_range Pattern&gt;
    requires view&lt;V&gt; &amp;&amp; view&lt;Pattern&gt; &amp;&amp;
             indirectly_comparable&lt;iterator_t&lt;V&gt;, iterator_t&lt;Pattern&gt;, ranges::equal_to&gt; &amp;&amp;
             (forward_range&lt;V&gt; || <i>tiny-range</i>&lt;Pattern&gt;)
  class lazy_split_view : public view_interface&lt;lazy_split_view&lt;V, Pattern&gt;&gt; {
    [&hellip;]
    <ins>constexpr bool empty() requires empty_checkable_range&lt;V&gt; &amp;&amp; 
                                    empty_checkable_range&lt;Pattern&gt;
    { return ranges::empty(<i>base_</i>) &amp;&amp; ranges::empty(<i>pattern_</i>); }
  
    constexpr bool empty() const requires empty_checkable_range&lt;const V&gt; &amp;&amp; 
                                          empty_checkable_range&lt;const Pattern&gt;
    { return ranges::empty(<i>base_</i>) &amp;&amp; ranges::empty(<i>pattern_</i>); }</ins>
  };
  [&hellip;]
}
            </pre>
      </blockquote>
    </li>
    <li>
      <p>Modify 26.7.17.2 <a href="https://eel.is/c++draft/range.split.view">[range.split.view]</a> as indicated:</p>
      <blockquote>
        <pre>
constexpr subrange&lt;iterator_t&lt;V&gt;&gt; <i>find-next</i>(iterator_t&lt;V&gt; it);
        </pre>
        <p>
-5- <i>Effects</i>: Equivalent to:
    <pre>    auto [b, e] = ranges::search(subrange(it, ranges::end(<i>base_</i>)), <i>pattern_</i>);
    if (b != ranges::end(<i>base_</i>) &amp;&amp; <ins>ranges::begin(<i>pattern_</i>) == ranges::end(<i>pattern_</i>)</ins><del>ranges::empty(<i>pattern_</i>)</del>) {
      ++b;
      ++e;
    }
    return {b, e};
        </pre>
        </p>
        </blockquote>    
    
    
    </li>
    <li>
      <p>Modify 26.7.19.2 <a href="https://eel.is/c++draft/range.common.view">[range.common.view]</a>, class template
        <tt>common_view</tt> synopsis, as indicated:</p>

      <blockquote>
        <pre>
namespace std::ranges {
  template&lt;view V&gt;
    requires (!common_range&lt;V&gt; &amp;&amp; copyable&lt;iterator_t&lt;V&gt;&gt;)
  class common_view : public view_interface&lt;common_view&lt;V&gt;&gt; {
    [&hellip;]
  
    <ins>constexpr bool empty() requires empty_checkable_range&lt;V&gt; { 
      return ranges::empty(<i>base_</i>); 
    }
    constexpr bool empty() const requires empty_checkable_range&lt;const V&gt; { 
      return ranges::empty(<i>base_</i>); 
    }</ins>

    constexpr auto size() requires sized_range&lt;V&gt; { 
      return ranges::size(<i>base_</i>); 
    }
    constexpr auto size() const requires sized_range&lt;const V&gt; { 
      return ranges::size(<i>base_</i>); 
    }
  };
  [&hellip;]
}
    </pre>
      </blockquote>
    </li>
    <li>
      <p>Modify 26.7.21.2 <a href="https://eel.is/c++draft/range.as.const.view">[range.as.const.view]</a>, class template
        <tt>as_const_view</tt> synopsis, as indicated:</p>

      <blockquote>
        <pre>
namespace std::ranges {
  template&lt;view V&gt;
    requires input_range&lt;V&gt;
  class as_const_view : public view_interface&lt;as_const_view&lt;V&gt;&gt; {
    [&hellip;]
  
    <ins>constexpr bool empty() requires empty_checkable_range&lt;V&gt;
    { return ranges::empty(<i>base_</i>); }
    constexpr bool empty() const requires empty_checkable_range&lt;const V&gt;
    { return ranges::empty(<i>base_</i>); }</ins>

    constexpr auto size() requires sized_range&lt;V&gt; { return ranges::size(<i>base_</i>); }
    constexpr auto size() const requires sized_range&lt;const V&gt; { return ranges::size(<i>base_</i>); }
  };
  [&hellip;]
}
      </pre>
      </blockquote>
    </li>
    <li>
      <p>Modify 26.7.22.2 <a href="https://eel.is/c++draft/range.elements.view">[range.elements.view]</a>, class template
        <tt>elements_view</tt> synopsis, as indicated:</p>

      <blockquote>
        <pre>
namespace std::ranges {
  [&hellip;]
  template&lt;input_range V, size_t N&gt;
    requires view&lt;V&gt; &amp;&amp; <i>has-tuple-element</i>&lt;range_value_t&lt;V&gt;, N&gt; &amp;&amp;
             <i>has-tuple-element</i>&lt;remove_reference_t&lt;range_reference_t&lt;V&gt;&gt;, N&gt; &amp;&amp;
             <i>returnable-element</i>&lt;range_reference_t&lt;V&gt;, N&gt;
  class elements_view : public view_interface&lt;elements_view&lt;V, N&gt;&gt; {
    [&hellip;]
  
    <ins>constexpr bool empty() requires empty_checkable_range&lt;V&gt;
    { return ranges::empty(<i>base_</i>); }
    constexpr bool empty() const requires empty_checkable_range&lt;const V&gt;
    { return ranges::empty(<i>base_</i>); }</ins>

    constexpr auto size() requires sized_range&lt;V&gt;
    { return ranges::size(<i>base_</i>); }
    constexpr auto size() const requires sized_range&lt;const V&gt;
    { return ranges::size(<i>base_</i>); }

    [&hellip;]
  };
  [&hellip;]
}
        </pre>
      </blockquote>
    </li>
    <li>
      <p>Modify 26.7.23.2 <a href="https://eel.is/c++draft/range.enumerate.view">[range.enumerate.view]</a>, class template
        <tt>enumerate_view</tt> synopsis, as indicated:</p>

      <blockquote>
        <pre>
namespace std::ranges {
  template&lt;view V&gt;
    requires <i>range-with-movable-references</i>&lt;V&gt;
  class enumerate_view : public view_interface&lt;enumerate_view&lt;V&gt;&gt; {
    [&hellip;]
  
    <ins>constexpr bool empty() requires empty_checkable_range&lt;V&gt;
    { return ranges::empty(<i>base_</i>); }
    constexpr bool empty() const requires empty_checkable_range&lt;const V&gt;
    { return ranges::empty(<i>base_</i>); }</ins>

    constexpr auto size() requires sized_range&lt;V&gt;
    { return ranges::size(<i>base_</i>); }
    constexpr auto size() const requires sized_range&lt;const V&gt;
    { return ranges::size(<i>base_</i>); }
    
    [&hellip;]
  };
  [&hellip;]
}
</pre>
</blockquote>
</li>
<li>
      <p>Modify 26.7.24.2 <a href="https://eel.is/c++draft/range.zip.view">[range.zip.view]</a>, class template
        <tt>zip_view</tt> synopsis, as indicated:</p>

      <blockquote>
        <pre>
namespace std::ranges {
  [&hellip;]
  template&lt;input_range... Views&gt;
    requires (view&lt;Views&gt; &amp;&amp; ...) &amp;&amp; (sizeof...(Views) > 0)
  class zip_view : public view_interface&lt;zip_view&lt;Views...&gt;&gt; {
    [&hellip;]
  
    <ins>constexpr bool empty() requires (empty_checkable_range&lt;Views&gt; &amp;&amp; ...);
    constexpr bool empty() requires (empty_checkable_range&lt;const Views&gt; &amp;&amp; ...);</ins>

    constexpr auto size() requires (sized_range&lt;Views&gt; &amp;&amp; ...);
    constexpr auto size() const requires (sized_range&lt;const Views&gt; &amp;&amp; ...);
    
  };
  [&hellip;]
}
</pre>
</blockquote>
      [&hellip;]
<pre><ins>  constexpr bool empty() requires (empty_checkable_range&lt;Views&gt; &amp;&amp; ...);
  constexpr bool empty() requires (empty_checkable_range&lt;const Views&gt; &amp;&amp; ...);</ins>
</pre>
<blockquote>
<p>
<ins>-?- <i>Effects</i>: Equivalent to:</ins>
<pre><ins>    return apply([](auto... is_empty) { return (is_empty || ...); }, 
                 <i>tuple-transform</i>(ranges::empty, <i>views_</i>));</ins>
</pre>
</ins>
</p>
</blockquote>
<pre>
  constexpr auto size() requires (sized_range&lt;Views&gt; &amp;&amp; ...);
  constexpr auto size() const requires (sized_range&lt;const Views&gt; &amp;&amp; ...);
</pre>
<blockquote>
<p>
-3- <i>Effects</i>: Equivalent to:
<pre>
    return apply([](auto... sizes) {
      using CT = <i>make-unsigned-like-t</i>&lt;common_type_t&lt;decltype(sizes)...&gt;&gt;;
      return ranges::min({CT(sizes)...});
    }, <i>tuple-transform</i>(ranges::size, <i>views_</i>));
</pre>
</p>
</blockquote>
</li>
<li>
<p>Modify 26.7.25.2 <a href="https://eel.is/c++draft/range.zip.transform.view">[range.zip.transform.view]</a>, class template
  <tt>zip_transform_view</tt> synopsis, as indicated:</p>

<blockquote>
  <pre>
namespace std::ranges {
  template&lt;move_constructible F, input_range... Views&gt;
    requires (view&lt;Views&gt; &amp;&amp; ...) &amp;&amp; (sizeof...(Views) > 0) &amp;&amp; is_object_v&lt;F&gt; &amp;&amp;
             regular_invocable&lt;F&amp;, range_reference_t&lt;Views&gt;...&gt; &amp;&amp;
             <i>can-reference</i>&lt;invoke_result_t&lt;F&amp;, range_reference_t&lt;Views&gt;...&gt;&gt;
  class zip_transform_view : public view_interface&lt;zip_transform_view&lt;F, Views...&gt;&gt; {
    [&hellip;]
    
    <ins>constexpr bool empty() requires empty_checkable_range&lt;<i>InnerView</i>&gt; {
      return <i>zip_</i>.empty();
    }

    constexpr bool empty() const requires empty_checkable_range&lt;const <i>InnerView</i>&gt; {
      return <i>zip_</i>.empty();
    }</ins>

    constexpr auto size() requires sized_range&lt;<i>InnerView</i>&gt; {
      return <i>zip_</i>.size();
    }

    constexpr auto size() const requires sized_range&lt;const <i>InnerView</i>&gt; {
      return <i>zip_</i>.size();
    }
  };
  [&hellip;]
}
    </pre>
      </blockquote>
    </li>
    <li>
      <p>Modify 26.7.28.2 <a href="https://eel.is/c++draft/range.chunk.view.input">[range.chunk.view.input]</a>, class
        template <tt>chunk_view</tt> synopsis for input ranges, as indicated:</p>
      <blockquote>
        <pre>
namespace std::ranges {
  [&hellip;]
  template&lt;view V&gt;
    requires input_range&lt;V&gt;
  class chunk_view : public view_interface&lt;chunk_view&lt;V&gt;&gt; {
    [&hellip;]

    <ins>constexpr bool empty() requires empty_checkable_range&lt;Vs&gt;;
    constexpr bool empty() const requires empty_checkable_range&lt;const V&gt;;</ins>

    constexpr auto size() requires sized_range&lt;V&gt;;
    constexpr auto size() const requires sized_range&lt;const V&gt;;
  };
  [&hellip;]
}
    </pre>
  </blockquote>
  [&hellip;]
<pre>
<ins>
  constexpr bool empty() requires empty_checkable_range&lt;Vs&gt;;
  constexpr bool empty() const requires empty_checkable_range&lt;const V&gt;;</ins>
</pre>
<blockquote>
<p>
<ins>-?- <i>Effects</i>: Equivalent to:</ins>
<pre><ins>    return ranges::empty(<i>base_</i>);</ins></pre>
</pre>
</ins>
</p>
</blockquote>
<pre>
  constexpr auto size() requires sized_range&lt;V&gt;;
  constexpr auto size() const requires sized_range&lt;const V&gt;;
</pre>
<blockquote>
<p>
-5- <i>Effects</i>: Equivalent to:
<pre>    return <i>to-unsigned-like</i>(<i>div-ceil</i>(ranges::distance(<i>base_</i>), <i>n_</i>));</pre>
</pre>
</p>
</blockquote>
    </li>
    <li>
      <p>Modify 26.7.31.2 <a href="https://eel.is/c++draft/range.stride.view">[range.stride.view]</a>, class
        template <tt>stride_view</tt> synopsis, as indicated:</p>
      <blockquote>
        <pre>
namespace std::ranges {
  template&lt;input_range V&gt;
    requires view&lt;V&gt;
  class stride_view : public view_interface&lt;stride_view&lt;V&gt;&gt; {
    [&hellip;]

    <ins>constexpr bool empty() requires empty_checkable_range&lt;V&gt;;
    constexpr bool empty() const requires empty_checkable_range&lt;const V&gt;;</ins>

    constexpr auto size() requires sized_range&lt;V&gt;;
    constexpr auto size() const requires sized_range&lt;const V&gt;;
    [&hellip;]
  };
  [&hellip;]
}
    </pre>
  </blockquote>
  [&hellip;]
<pre>
<ins>
  constexpr bool empty() requires empty_checkable_range&lt;V&gt;;
  constexpr bool empty() const requires empty_checkable_range&lt;const V&gt;;</ins>
</pre>
<blockquote>
<p>
<ins>-?- <i>Effects</i>: Equivalent to:</ins>
<pre><ins>    return ranges::empty(<i>base_</i>);</ins></pre>
</p>
</blockquote>
<pre>
  constexpr auto size() requires sized_range&lt;V&gt;;
  constexpr auto size() const requires sized_range&lt;const V&gt;;
</pre>
<blockquote>
<p>
-5- <i>Effects</i>: Equivalent to:<pre>    return <i>to-unsigned-like</i>(<i>div-ceil</i>(ranges::distance(<i>base_</i>), <i>stride_</i>));</pre>
</pre>
</p>
</blockquote>
    </li>
    <li>
      <p>Modify 26.7.32.2 <a href="https://eel.is/c++draft/range.cartesian.view">[range.cartesian.view]</a>, class template
        <tt>cartesian_product_view</tt> synopsis, as indicated:</p>
      <blockquote>
        <pre>
namespace std::ranges {
  [&hellip;]
  template&lt;input_range First, forward_range... Vs&gt;
    requires (view&lt;First&gt; &amp;&amp; ... &amp;&amp; view&lt;Vs&gt;)
  class cartesian_product_view : public view_interface&lt;cartesian_product_view&lt;First, Vs...&gt;&gt; {
    [&hellip;]
  
    <ins>constexpr bool empty() requires (empty_checkable_range&lt;First&gt; &amp;&amp; ... &amp;&amp;
                                     empty_checkable_range&lt;Vs&gt;);
    constexpr bool empty() const requires (empty_checkable_range&lt;const First&gt; &amp;&amp; ... &amp;&amp;
                                           empty_checkable_range&lt;const Vs&gt;);</ins>

    constexpr <i>see below</i> size()
      requires <i>cartesian-product-is-sized</i>&lt;First, Vs...&gt;;
    constexpr <i>see below</i> size() const
      requires <i>cartesian-product-is-sized</i>&lt;const First, const Vs...&gt;;
    
    [&hellip;]
  };
  [&hellip;]
}    
</pre>
      </blockquote>
      [&hellip;]
      <pre>
  constexpr <i>iterator</i>&lt;false&gt; end()
    requires ((!<i>simple-view</i>&lt;First&gt; || ... || !<i>simple-view</i>&lt;Vs&gt;)
      &amp;&amp; <i>cartesian-product-is-common</i>&lt;First, Vs...&gt;);
  constexpr <i>iterator</i>&lt;true&gt; end() const
    requires <i>cartesian-product-is-common</i>&lt;const First, const Vs...&gt;;
        </pre>
        <blockquote>
        <p>
        -4- Let:
        </p>
        <ol style="list-style-type: none">
        <li><p>(4.1) &mdash; <code><i>is-const</i></code> be <code>true</code> for the const-qualified overload, and <code>false</code> otherwise;</p></li>
        <li><p>(4.2) &mdash; <code><i>is-empty</i></code> be <code>true</code> if the expression <ins><code>ranges::begin(rng) == ranges::end(rng)</code></ins><del><code>ranges::empty(rng)</code></del> is <code>true</code> 
        for any <code>rng</code> among the underlying ranges except the first one and <code>false</code> otherwise; and</p></li>
        <li><p>(4.3) &mdash; <code><i>begin-or-first-end</i>(rng)</code> be expression-equivalent to 
        <code><i>is-empty</i> ? ranges::begin(rng) : <i>cartesian-common-arg-end</i>(rng)</code> 
        if <code>rng</code> is the first underlying range and <code>ranges::begin(rng)</code>
        otherwise.</p></li>
        </ol>
        <p>
        -5- <i>Effects</i>: Equivalent to:
        </p>
        <blockquote><pre>
        iterator&lt;<i>is-const</i>&gt; it(<i>tuple-transform</i>(
          [](auto&amp; rng){ return <i>begin-or-first-end</i>(rng); }, <i>bases_</i>));
        return it;
        </pre></blockquote>
        </blockquote>
        <pre>
  constexpr default_sentinel_t end() const noexcept;
        </pre>
        <blockquote>
          <p>-6- <i>Returns</i>: <tt>default_sentinel</tt>.</p>
        </blockquote>
<pre>
  <ins>
  constexpr bool empty() requires (empty_checkable_range&lt;First&gt; &amp;&amp; ... &amp;&amp;
                                   empty_checkable_range&lt;Vs&gt;);
  constexpr bool empty() const requires (empty_checkable_range&lt;const First&gt; &amp;&amp; ... &amp;&amp;
                                         empty_checkable_range&lt;const Vs&gt;);</ins>
</pre>
<blockquote>
<p>
<ins>-?- <i>Effects</i>: Equivalent to:</ins>
<pre><ins>    return apply([](auto... is_empty) { return (is_empty || ...); }, 
                 <i>tuple-transform</i>(ranges::empty, <i>bases_</i>));</ins>
</pre>
</ins>
</p>
</blockquote>
  </ol>
  </div>
</body>

</html>