<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title><code>views::set_<i>operations</i></code></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>P3741R0</td>
      </tr>
      <tr>
        <td>Date</td>
        <td>2025-06-21</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><code>views::set_<i>operations</i></code></h1>
  <ul>

    <li>Abstract</li>
    <li>Revision history</li>
    <li>Motivation</li>
    <li>Design</li>
    <li>Implementation experience</li>
    <li>Wording</li>
    <li>References</li>

  </ul>
  <a name="Abstract"></a>
  <h2>Abstract</h2>
  <p>
    This paper proposes four range adaptors for set operations rated as
    Tier 3 in <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html">P2760</a>, namely
    <code>views::set_difference</code>,
    <code>views::set_intersection</code>,
    <code>views::set_union</code>, and <code>views::set_symmetric_difference</code>,
    to extend the breadth of
    Ranges.
  </p>
  <p>These adaptors complement existing set algorithms by enabling composable, lazy, and allocation-free views
    for common set operations. They fill a notable gap in the Ranges library for many practical applications.
  </p>
  <a name="Revision history"></a>
  <h2>Revision history</h2>
  <p>
  <h3>R0</h3>
  <p>Initial revision.</p>
  </p>
  </ol>
  </p>
  <a name="Motivation"></a>
  <h2>Motivation</h2>
  <p>
    Although there are corresponding constrained algorithm versions of set operations, they all need to output the
    results to
    some sort of output range.
    This brings advantages of the view's lazy evaluation:
    we can construct set elements on
    the fly without allocating memory in advance.</p>
  <p>
    Given that set operations are extremely common in the real world, introducing corresponding range adaptors is
    valuable
    and facilitates the user experience with Ranges:</p>
  <pre>
  <i>/* algorithm approach */</i>
  std::vector&lt;int&gt; diff;
  ranges::set_difference(v1, v2, std::back_inserter(diff));

  <i>/* view approach, no allocation, composable */</i>
  auto diff = v1 | views::set_difference(v2);</pre>
  </ol>
  <a name="Design"></a>
  <h2>Design</h2>
  <p>
  <h3>Pipe syntax support</h3>
  <p>
    It is reasonable that <code>views::set_<i>operations</i></code> should be range adaptor objects instead of just CPOs.
    This allows expressions like <code>rng1 |
      views::set_<i>operations</i>(rng2)</code>, meaning to extract elements from <code>rng1</code> through the set
    operation with <code>rng2</code>, which is
    intuitive and worth supporting.
  </p>
  <h3>
    Constraint for <code>set_[intersection|difference]_view</code>
  </h3>
  <div style="display: flex; justify-content: flex-start; gap: 16px;">
    <!-- Intersection -->
    <svg width="230" height="190">
      <text x="115" y="38" font-size="20" fill="#333" text-anchor="middle" font-weight="bold">Intersection</text>
      <!-- Both circles -->
      <circle cx="85" cy="105" r="55" fill="#fff" />
      <circle cx="145" cy="105" r="55" fill="#fff" />
      <!-- Overlapping area filled for intersection -->
      <defs>
        <clipPath id="clipInterA">
          <circle cx="85" cy="105" r="55" />
        </clipPath>
      </defs>
      <g clip-path="url(#clipInterA)">
        <circle cx="145" cy="105" r="55" fill="#ffb3b3" />
      </g>
      <!-- Circle borders -->
      <circle cx="85" cy="105" r="55" fill="none" stroke="#888" stroke-width="2" />
      <circle cx="145" cy="105" r="55" fill="none" stroke="#888" stroke-width="2" />
      <text x="85" y="180" font-size="18" fill="#333" text-anchor="middle">A</text>
      <text x="145" y="180" font-size="18" fill="#333" text-anchor="middle">B</text>
    </svg>

    <!-- Difference (A - B), now in the middle, with A and B labels -->
    <svg width="230" height="190">
      <text x="115" y="38" font-size="20" fill="#333" text-anchor="middle" font-weight="bold">Difference</text>
      <circle cx="85" cy="105" r="55" fill="#ffb3b3" />
      <circle cx="145" cy="105" r="55" fill="#fff" />
      <defs>
        <clipPath id="clipDiffA">
          <circle cx="145" cy="105" r="55" />
        </clipPath>
      </defs>
      <g clip-path="url(#clipDiffA)">
        <circle cx="85" cy="105" r="55" fill="#fff" />
      </g>
      <circle cx="85" cy="105" r="55" fill="none" stroke="#888" stroke-width="2" />
      <circle cx="145" cy="105" r="55" fill="none" stroke="#888" stroke-width="2" />
      <!-- Correct labels: A on right, B on left -->
      <text x="85" y="180" font-size="18" fill="#333" text-anchor="middle">A</text>
      <text x="145" y="180" font-size="18" fill="#333" text-anchor="middle">B</text>
    </svg>

    <!-- Difference (A - B), now at the right -->
    <svg width="230" height="190">
      <text x="115" y="38" font-size="20" fill="#333" text-anchor="middle" font-weight="bold">Difference</text>
      <circle cx="145" cy="105" r="55" fill="#ffb3b3" />
      <circle cx="85" cy="105" r="55" fill="#fff" />
      <defs>
        <clipPath id="clipDiffB">
          <circle cx="85" cy="105" r="55" />
        </clipPath>
      </defs>
      <g clip-path="url(#clipDiffB)">
        <circle cx="145" cy="105" r="55" fill="#fff" />
      </g>
      <circle cx="85" cy="105" r="55" fill="none" stroke="#888" stroke-width="2" />
      <circle cx="145" cy="105" r="55" fill="none" stroke="#888" stroke-width="2" />
      <text x="85" y="180" font-size="18" fill="#333" text-anchor="middle">B</text>
      <text x="145" y="180" font-size="18" fill="#333" text-anchor="middle">A</text>
    </svg>
  </div>

  <p>
    For <code>
      views::set_<i>operations</i>(A, B)</code>, both intersection and difference produce only elements from A, so the
    result is just a subset of A;
    the element type of B is irrelevant for output, only its order matters. The only thing that matters is making sure
    the elements of both
    ranges are strictly weakly ordered so that they can be compared meaningfully.</p>
  <p>
    The standard already has a
    concept for this, namely <code>indirect_strict_weak_order</code>, which is also
    used for the corresponding constrained algorithm (a component of <code>mergeable</code> concept). The signatures of
    the
    two
    classes would be:
  </p>
  <pre>
  template&lt;view V1, view V2&gt;
    requires input_range&lt;V1&gt; && input_range&lt;V2&gt; &&
             indirect_strict_weak_order&lt;ranges::less, iterator_t&lt;V1&gt;, iterator_t&lt;V2&gt;&gt;
  class set_[intersection|difference]_view;
  </pre>
  <h3>
    Constraint for <code>set_[union|symmetric_difference]_view</code>
  </h3>
  <div style="display: flex; justify-content: flex-start; gap: 16px;">
    <!-- Union -->
    <svg width="230" height="190">
      <!-- Title -->
      <text x="115" y="38" font-size="20" fill="#333" text-anchor="middle" font-weight="bold">Union</text>
      <!-- Both circles filled for union -->
      <circle cx="85" cy="105" r="55" fill="#ffb3b3" />
      <circle cx="145" cy="105" r="55" fill="#ffb3b3" />
      <!-- Circle borders -->
      <circle cx="85" cy="105" r="55" fill="none" stroke="#888" stroke-width="2" />
      <circle cx="145" cy="105" r="55" fill="none" stroke="#888" stroke-width="2" />
      <!-- Set labels -->
      <text x="85" y="180" font-size="18" fill="#333" text-anchor="middle">A</text>
      <text x="145" y="180" font-size="18" fill="#333" text-anchor="middle">B</text>
    </svg>

    <!-- Symmetric Difference -->
    <svg width="230" height="190">
      <!-- Title -->
      <text x="115" y="38" font-size="20" fill="#333" text-anchor="middle" font-weight="bold">Symmetric
        Difference</text>
      <!-- Both circles filled for symmetric difference -->
      <circle cx="85" cy="105" r="55" fill="#ffb3b3" />
      <circle cx="145" cy="105" r="55" fill="#ffb3b3" />
      <!-- Overlapping area masked with white for symmetric difference -->
      <defs>
        <clipPath id="clipSymA">
          <circle cx="85" cy="105" r="55" />
        </clipPath>
        <clipPath id="clipSymB">
          <circle cx="145" cy="105" r="55" />
        </clipPath>
      </defs>
      <g clip-path="url(#clipSymA)">
        <circle cx="145" cy="105" r="55" fill="#fff" clip-path="url(#clipSymB)" />
      </g>
      <!-- Circle borders -->
      <circle cx="85" cy="105" r="55" fill="none" stroke="#888" stroke-width="2" />
      <circle cx="145" cy="105" r="55" fill="none" stroke="#888" stroke-width="2" />
      <!-- Set labels -->
      <text x="85" y="180" font-size="18" fill="#333" text-anchor="middle">A</text>
      <text x="145" y="180" font-size="18" fill="#333" text-anchor="middle">B</text>
    </svg>
  </div>
  <p>
    Unlike the above, union and symmetric difference produce elements from both A and B. In addition to
    ensuring that both ranges are strictly weakly ordered, we also need to ensure that the element types of both ranges
    are somehow compatible.
  </p>
  <p>
    Thanks to the fact that it is already <code>concat_view</code> in the standard,
    the <code><i>concatable</i></code> concept is perfect for such a purpose. The signatures of both would be:
  </p>
  <pre>
  template&lt;view V1, view V2&gt;
    requires input_range&lt;V1&gt; && input_range&lt;V2&gt; &&
             indirect_strict_weak_order&lt;ranges::less, iterator_t&lt;V1&gt;, iterator_t&lt;V2&gt;&gt; &&
             <i>concatable</i>&lt;V1, V2&gt;
  class set_[union|symmetric_difference]_view;
    </pre>
  <h3>Iterator design for <code>set_[intersection|difference]_view</code>
  </h3>
  <p>
    The iterators of the four views all follow a similar design. When they are constructed, we first find the next valid
    element through the <code><i>satisfy</i>()</code> function, and then in each <code>operator++()</code>, we increment
    the
    underlying iterator and
    call the <code><i>satisfy</i>()</code> again to find the next valid element, and so on.
  </p>
  <p>
    Since in <code><i>satisfy</i>()</code>, we also need to check whether the iterators of A or B have reached the end
    to determine the next valid element, we need to know the information of the sentinels of both, which means we
    need to store both sentinels in the iterator.
  </p>
  <p>
    For <code>set_intersection_view</code>, its iterator signature is as follows:</p>
  <pre>
  class set_intersection_view::<i>iterator</i> {
    iterator_t&lt;V1&gt; <i>current1_</i>;
    sentinel_t&lt;V1&gt; <i>end1_</i>;
    iterator_t&lt;V2&gt; <i>current2_</i>;
    sentinel_t&lt;V2&gt; <i>end2_</i>;

    constexpr void
    <font style="color: red;"><i>satisfy</i>()</font> {
      while (<i>current1_</i> != <i>end1_</i> && <i>current2_</i> != <i>end2_</i>) {
        <font style="color: red;"> /* Find the next valid element in the first range */ </font>
      }
    }

    constexpr <i>iterator</i>(iterator_t&lt;V1&gt; current1, sentinel_t&lt;V2&gt; end1,
                       iterator_t&lt;V1&gt; current2, sentinel_t&lt;V2&gt; end2)
      : <i>current1_</i>(std::move(current1)), <i>end1_</i>(end1),
        <i>current2_</i>(std::move(current2)), <i>end2_</i>(end2) {
      <font style="color: red;"><i>satisfy</i>()</font>;
    } 

  public:
    constexpr decltype(auto) operator*() const { return *<i>current1_</i>; }

    constexpr <i>iterator</i>&
    operator++() {
      ++<i>current1_</i>;
      ++<i>current2_</i>;
      <font style="color: red;"><i>satisfy</i>()</font>;
      return *this;
    }

    friend constexpr bool
    operator==(const <i>iterator</i>& x, default_sentinel_t) {
      return x.<i>current1_</i> == x.<i>end1_</i> || x.<i>current2_</i> == x.<i>end2_</i>;
    }
  };
  </pre>
  We can slightly modify the logic of the three functions above, <code><i>satisfy</i>()</code>,
  <code>operator++()</code>, and <code>operator==()</code>, to make a corresponding iterator for
  <code>set_difference_view</code>:
  <pre>
  class set_difference_view::<i>iterator</i> {
    iterator_t&lt;V1&gt; <i>current1_</i>;
    sentinel_t&lt;V1&gt; <i>end1_</i>;
    iterator_t&lt;V2&gt; <i>current2_</i>;
    sentinel_t&lt;V2&gt; <i>end2_</i>;

    constexpr void
    <i>satisfy</i>() {
      while (<i>current1_</i> != <i>end1_</i> && <i>current2_</i> != <i>end2_</i>) {
        <font style="color: red;">/* New condition to find the next valid element in the first range */</font>
      }
    }

    constexpr <i>iterator</i>(iterator_t&lt;V1&gt; current1, sentinel_t&lt;V2&gt; end1,
                       iterator_t&lt;V1&gt; current2, sentinel_t&lt;V2&gt; end2)
      : <i>current1_</i>(std::move(current1)), <i>end1_</i>(end1),
        <i>current2_</i>(std::move(current2)), <i>end2_</i>(end2) {
      satisfy();
    } 

  public:
    constexpr decltype(auto) operator*() const { return *<i>current1_</i>; }

    constexpr <i>iterator</i>&
    operator++() {
      ++<i>current1_</i>;
      <del>++<i>current2_</i>;</del>
      <i>satisfy</i>();
      return *this;
    }

    friend constexpr bool
    operator==(const <i>iterator</i>& x, default_sentinel_t) {
      return x.<i>current1_</i> == x.<i>end1_</i> <del>|| x.<i>current2_</i> == x.<i>end2_</i></del>;
    }
  };
    </pre>
  <h3>Iterator design for <code>set_[union|symmetric_difference]_view</code>
  </h3>
  <p>For <code>set_union_view</code>'s iterator, it is necessary to know which underlying iterator is active right now,
    so an additional flag is need to indicate that.</p>
  <p>
    In addition, since the resulting set contains elements from two different ranges, the new reference type needs to be
    a common reference of the two, in which case <code><i>concat-reference-t</i></code> nicely fits
    the
    purpose:
  </p>
  <pre>
    class set_union_view::<i>iterator</i> {
      iterator_t&lt;V1&gt; <i>current1_</i>;
      sentinel_t&lt;V1&gt; <i>end1_</i>;
      iterator_t&lt;V2&gt; <i>current2_</i>;
      sentinel_t&lt;V2&gt; <i>end2_</i>;
      <font style="color: red;">bool <i>use_first_</i>;</font>
  
      constexpr void
      <i>satisfy</i>() {
        <font style="color: red;">/* Find the next valid element from two ranges */</font>
      }
  
      constexpr <i>iterator</i>(iterator_t&lt;V1&gt; current1, sentinel_t&lt;V2&gt; end1,
                         iterator_t&lt;V1&gt; current2, sentinel_t&lt;V2&gt; end2)
        : <i>current1_</i>(std::move(current1)), <i>end1_</i>(end1),
          <i>current2_</i>(std::move(current2)), <i>end2_</i>(end2) {
        satisfy();
      } 
  
    public:
      constexpr <font style="color: red;"><i>concat-reference-t</i>&lt;V1, V2&gt;</font>
      operator*() const {
        if (<i>use_first_</i>)
          return *<i>current1_</i>;
        return *<i>current2_</i>;
      }

      constexpr iterator&
      operator++() {
        if (<i>use_first_</i>)
          ++<i>current1_</i>;
        else
          ++<i>current2_</i>;
        <i>satisfy</i>();
        return *this;
      }
  
      friend constexpr bool
      operator==(const <i>iterator</i>& x, default_sentinel_t) {
        return x.<i>current1_</i> == x.<i>end1_</i> && x.<i>current2_</i> == x.<i>end2_</i>;
      }
    };
      </pre>
  Similarly, we can make <code>set_symmetric_difference_view</code>'s iterator by modifying
  <code><i>satisfy()</i></code> to skip the invalid part.
  <h3>
    Borrowed ranges</h3>
  <p>

    <code>set_<i>operation</i>_view::iterator</code> only store iterator-sentinel pairs of two underlying views,
    so it is a <code>borrowed_range</code> when both models <code>borrowed_range</code>.
  </p>
  <h3>
    Non-common ranges</h3>
  <p>
    These view classes already have the origin sentinel stored as a member in its iterator, so their <code>end()</code>
    can simply return
    <code>default_sentinel</code>, which makes them always non-common ranges. There is no need to
    compromise
    compilation and runtime performance to just support common ranges.
  </p>
  <h3>
    Only-forward ranges
  </h3>
  <p>Although supporting bidirectional ranges is theoretically possible, it would introduce potential use-after-move
    issues due to value comparisons between the two ranges.
    For
    example, applying <code>views::reverse | views::as_rvalue</code> with it is an undefined behavior.</p>
  <p>
    The author does not plan to support this, this is consistent
    with <a
      href="https://github.com/ericniebler/range-v3/blob/master/include/range/v3/view/set_algorithm.hpp">range/v3</a>.
  </p>
  <h3>
    Non <code>const</code>-iterable
  </h3>
  <p>
    Except for set union, <code><i>satisfy()</i></code> for other operations has the worst complexity of O(n), because
    we need to skip the invalid white area to locate the next valid element. In order to ensure the amortized constant
    time
    complexity of
    <code>begin()</code> required by the <code>range</code> concept, we need to cache the
    iterator in the first call to <code>begin()</code>, which means that other view classes except
    <code>set_union_view</code> are
    not <code>const</code>-iterable.
  </p>
  <p>However, it is worth noting that when A or B is only an input range, those views will be an input range whose
    <code>begin()</code> is only allowed to be called once, in which case no cache is needed, making it feasible
    to provide <code>const begin</code>.
  </p>
  <p>Given that the current standard does not support <code>const</code>-iterable in such an aggressive way,
    for example <code>filter_view</code> and
    <code>drop_while_view</code> do not choose to do so, the author does not provide such support to be
    consistent.
  </p>
  <p>Here is the summary table:
  </p>
  <table class="bordered">
    <thead>
      <tr>
        <th>View</th>
        <th><code>const</code>-Iterable</th>
        <th>Caches <code>begin()</code></th>
        <th>Complexity</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><code>set_difference_view</code></td>
        <td>❌</td>
        <td>✅</td>
        <td>Amortized constant</td>
      </tr>
      <tr>
        <td><code>set_intersection_view</code></td>
        <td>❌</td>
        <td>✅</td>
        <td>Amortized constant</td>
      </tr>
      <tr>
        <td><code>set_union_view</code></td>
        <td>✅</td>
        <td>❌</td>
        <td>Constant</td>
      </tr>
      <tr>
        <td><code>set_symmetric_difference_view</code></td>
        <td>❌</td>
        <td>✅</td>
        <td>Amortized constant</td>
      </tr>
    </tbody>
  </table>
  <h3>
    No customized comparison and projection
  </h3>
  <p>
    Although those views in range/v3 also support customized comparison and projection, i.e.,
    <code>views::set_<i>operations</i>(rng1, rng2, pred, proj1, proj2)</code>, the author does not think
    this is a good idea. This is inconsistent with the standard design guideline.
  </p>
  <p>Custom comparisons and projections should be associated with constraint algorithms rather than range adaptors. In
    fact, the standard never expected any range adaptor to support this, even though some of them may be doable, such
    as
    <code>filter_view</code> or <code>chunk_by_view</code>, which only support custom predicates and do not extend
    support for projections.
    As for <code>split_view</code>, the standard also explicitly specifies <code>ranges::equal_to</code> as the
    comparison function, leaving no room for custom specification.
  </p>
  <p>
    Because this is an expensive tradeoff, as it requires three extra fields per iterator to store the functions.
    Additionally,
    we cannot store these functions in the view because this prevents
    the view from being borrowed, which is not ideal. Why bother ourselves if there are more consistent ways
    with
    <code>views::transform</code> to do the equivalent?
  </p>
  <p>
    Also, supporting such unnecessary flexibility can bring confusion. For example, it might be unclear what
    <code>views::set_operation(rng1, x)</code> means, since <code>x</code> could be either a range or a predicate.
  </p>
  <p>Not sure why range/v3 was designed this way, but the authors strongly prefer not to support it and intuitively
    chose <code>ranges::less</code> for comparing.
  </p>
  <h3>
    No <code>reserve_hint</code> member
  </h3>
  <p>While we could easily get an upper bound on the resulting view size to provide a <code>reserve_hint</code>
    member, the actual size value depends heavily on the sizes of A and B and the actual value of their elements, which
    means it could differ too much from the upper bound. In this case, the author chose not to provide a
    <code>reserve_hint</code> member.
  </p>
  <h3>
    No customized <code>iter_swap</code> specializations
  </h3>
  <p>It doesn't make sense to provide <code>iter_swap</code> specializations for these new iterators, since we'd
    break the origin
    order by swapping the elements which leads to undefined behavior.</p>
  <h3>Check for sorted ranges</h3>
  <p>Set operations require both ranges to be sorted, so a precondition needs to be imposed in the constructors to
    ensure correct semantics.</p>
  <h3>
    Provide <code>base</code> member
  </h3>
  <p>
    <code>set_[intersection|difference]_view</code> can be seen as a subset of the first range,
    so it makes sense to provide <code>base()</code> members for it and its iterator to access the first
    underlying view and iterator.
  </p>
  <a name="Implementation experience"></a>
  <h2>Implementation experience</h2>
  <p>The author implemented four <code>views::set_<i>operations</i></code>s based on libstdc++, see <a
      href="https://godbolt.org/z/T4j8Ksfj3">godbolt</a>.</p>
  <a name="Wording"></a>
  <h2>Wording</h2>
  <p>This wording is relative to the <a href="https://eel.is/c++draft">latest working draft</a>.
  </p>
  </div>
  <div>
    <ol>
      <ol>
        <li>
          <p>Add one new feature-test macro to 17.3.2 <a href="https://eel.is/c++draft/version.syn">[version.syn]</a>:
          </p>
          <blockquote>
            <pre><ins>#define __cpp_lib_ranges_set_view 20XXXXL // <i>freestanding, also in</i> &lt;ranges&gt;</ins></pre>
          </blockquote>
        </li>

        <li>
          <p>Modify 25.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;]
  namespace views { inline constexpr <i>unspecified</i> to_input = <i>unspecified</i>; }

<ins>  // <i>[range.set.difference]</a>, set difference view</i>
  template&lt;view V1, view V2&gt;
    requires <i>see below</i>
  class set_difference_view;

  template&lt;view V1, view V2&gt;
    constexpr bool enable_borrowed_range&lt;set_difference_view&lt;V1, V2&gt;&gt; =
      enable_borrowed_range&lt;V1&gt; && enable_borrowed_range&lt;V2&gt;;

  namespace views { inline constexpr <i>unspecified</i> set_difference = <i>unspecified</i>; }

  // <i>[range.set.intersection]</a>, set intersection view</i>
  template&lt;view V1, view V2&gt;
    requires <i>see below</i>
  class set_intersection_view;

  template&lt;view V1, view V2&gt;
    constexpr bool enable_borrowed_range&lt;set_intersection_view&lt;V1, V2&gt;&gt; =
      enable_borrowed_range&lt;V1&gt; && enable_borrowed_range&lt;V2&gt;;

  namespace views { inline constexpr <i>unspecified</i> set_intersection = <i>unspecified</i>; }

  // <i>[range.set.union]</a>, set union view</i>
  template&lt;view V1, view V2&gt;
    requires <i>see below</i>
  class set_union_view;

  template&lt;view V1, view V2&gt;
    constexpr bool enable_borrowed_range&lt;set_union_view&lt;V1, V2&gt;&gt; =
      enable_borrowed_range&lt;V1&gt; && enable_borrowed_range&lt;V2&gt;;

  namespace views { inline constexpr <i>unspecified</i> set_union = <i>unspecified</i>; }

  // <i>[range.set.symmetric.difference]</a>, set symmetric difference view</i>
  template&lt;view V1, view V2&gt;
    requires <i>see below</i>
  class set_symmetric_difference_view;

  template&lt;view V1, view V2&gt;
    constexpr bool enable_borrowed_range&lt;set_symmetric_difference_view&lt;V1, V2&gt;&gt; =
      enable_borrowed_range&lt;V1&gt; && enable_borrowed_range&lt;V2&gt;;

  namespace views { inline constexpr <i>unspecified</i> set_symmetric_difference = <i>unspecified</i>; }</ins>
}
        </pre>
          </blockquote>
        </li>
        <li>
          <p>Add <b>25.7.? Set difference view [range.set.difference]</b></a> after 25.7.35 <a
              href="https://eel.is/c++draft/range.to.input">[range.to.input]</a> as indicated:</p>
          <p>[25.7.?.1] Overview [range.set.difference.overview]</p>
          <p>
            -1- <code>set_difference_view</code> presents a view of set difference between two sorted ranges.
          </p>
          <p>
            -2- The name <code>views::set_difference</code> denotes a range adaptor object (25.7.2 <a
              href="https://eel.is/c++draft/range.adaptor.object">[range.adaptor.object]</a>).
            Given subexpressions <code>E</code> and F, the expression <code>views::set_difference(E, F)</code> is
            expression-equivalent to <code>set_difference_view(E, F)</code>.
          </p>
          <p>-3- [<i>Example 1</i>:</p>
          <pre>
vector v1{1, 2, 5, 5, 5,    9};
vector v2{   2, 5,       7};
println("{}", views::set_difference(v1, v2)); // <i>prints</i> [1, 5, 5, 9]</pre>
          — <i>end example]</i>
          <p>[25.7.?.2] Class template <code>set_difference_view</code> [range.set.difference.view]</p>
          <pre>
namespace std::ranges {
  template&lt;class R1, class R2&gt;
    concept <i>set-operable</i> = input_range&lt;R1&gt; && input_range&lt;R2&gt; &&    // <i>exposition only</i>
      indirect_strict_weak_order&lt;ranges::less, iterator_t&lt;R1&gt;, iterator_t&lt;R2&gt;>;

  template&lt;view V1, view V2&gt;
    requires <i>set-operable</i>&lt;V1, V2&gt;
  class set_difference_view : public view_interface&lt;set_difference_view&lt;V1, V2&gt;&gt; {
    V1 <i>base1_</i> = V1();        // <i>exposition only</i>
    V2 <i>base2_</i> = V2();        // <i>exposition only</i>
 
    // <i>[range.set.difference.iterator], class</i> set_difference_view::<i>iterator</i>
    class <i>iterator</i>;          // <i>exposition only</i>

  public:
    set_difference_view()
      requires default_initializable&lt;V1&gt; && default_initializable&lt;V2&gt; = default;

    constexpr explicit set_difference_view(V1 base1, V2 base2);

    constexpr V1 base() const & requires copy_constructible&lt;V1&gt; { return <i>base1_</i>; }
    constexpr V1 base() && { return std::move(<i>base1_</i>); }

    constexpr <i>iterator</i> begin();

    constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
  };
  
  template&lt;class R1, class R2&gt;
    set_difference_view(R1&amp;&amp;, R2&amp;&amp;)
      -&gt; set_difference_view&lt;views::all_t&lt;R1&gt;, views::all_t&lt;R2&gt;&gt;;
}
</pre>
          <pre>constexpr explicit set_difference_view(V1 base1, V2 base2);</pre>
          <blockquote>
            <p>-1- <i>Effects</i>: Initializes <code><i>base1_</i></code> with <code>std::move(base1)</code> and
              <code><i>base2_</i></code> with <code>std::move(base2)</code>.
              The behavior is undefined if either <code>ranges::is_sorted(<i>base1_</i>)</code> or
              <code>ranges::is_sorted(<i>base2_</i>)</code> is <code>false</code>.
            </p>
          </blockquote>
          <pre>constexpr <i>iterator</i> begin();</pre>
          <blockquote>
            <p>-2- <i>Returns</i>: <code>{ranges::begin(<i>base1_</i>), ranges::end(<i>base1_</i>), ranges::begin(<i>base2_</i>),
ranges::end(<i>base2_</i>)}</code>.
            </p>
            <p>-3- <i>Remarks</i>: In order to provide the amortized constant time complexity required by the
              <code>range</code>
              concept when <code>set_difference_view</code> models <code>forward_range</code>, this function caches
              the
              result within the <code>set_difference_view</code> for
              use on subsequent calls.
            </p>
          </blockquote>
          <p>[25.7.?.2] Class <code>set_difference_view::<i>iterator</i></code> [range.set.difference.iterator]</p>
          <pre>
namespace std::ranges {
  template&lt;view V1, view V2&gt;
    requires <i>set-operable</i>&lt;V1, V2&gt;
  class set_difference_view&lt;V1, V2&gt;&gt;::<i>iterator</i> {
    iterator_t&lt;V1&gt; <i>current1_</i> = iterator_t&lt;V1&gt;();    // <i>exposition only</i>
    sentinel_t&lt;V1&gt; <i>end1_</i>     = sentinel_t&lt;V1&gt;();    // <i>exposition only</i>
    iterator_t&lt;V2&gt; <i>current2_</i> = iterator_t&lt;V2&gt;();    // <i>exposition only</i>
    sentinel_t&lt;V2&gt; <i>end2_</i>     = sentinel_t&lt;V2&gt;();    // <i>exposition only</i>

    constexpr void <i>satisfy</i>();                       // <i>exposition only</i>

    constexpr <i>iterator</i>(iterator_t&lt;V1&gt; current1, sentinel_t&lt;V1&gt; end1,    // <i>exposition only</i>
                       iterator_t&lt;V2&gt; current2, sentinel_t&lt;V2&gt; end2);

  public:
    using iterator_category = <i>see below</i>;                        // <i>not always present</i>
    using iterator_concept  =
      conditional_t&lt;forward_range&lt;V1&gt; && forward_range&lt;V2&gt;, forward_iterator_tag, input_iterator_tag&gt;;
    using value_type        = range_value_t&lt;V1&gt;;
    using difference_type   = range_difference_t&lt;V1&gt;;

    <i>iterator</i>()
      requires default_initializable&lt;iterator_t&lt;V1&gt;&gt; && default_initializable&lt;iterator_t&lt;V2&gt;&gt;
        = default;

    constexpr iterator_t&lt;V1&gt; base() && { return std::move(<i>current1_</i>); }
    constexpr const iterator_t&lt;V1&gt;& base() const & noexcept { return <i>current1_</i>; }

    constexpr decltype(auto) operator*() const { return *<i>current1_</i>; }

    constexpr <i>iterator</i>& operator++();
    constexpr void operator++(int);
    constexpr <i>iterator</i> operator++(int) requires forward_range&lt;V1&gt; && forward_range&lt;V2&gt;;

    friend constexpr bool operator==(const <i>iterator</i>& x, const <i>iterator</i>& y)
      requires equality_comparable&lt;iterator_t&lt;V1&gt;&gt;;
    friend constexpr bool operator==(const <i>iterator</i>& x, default_sentinel_t);

    friend constexpr decltype(auto) iter_move(const <i>iterator</i>& i)
      noexcept(noexcept(ranges::iter_move(i.<i>current1_</i>)));
  };
}
</pre>
          <blockquote>
            <p>-1- The member <i>typedef-name</i> <code>iterator_category</code> is defined if and only if
              <code>V1</code> and <code>V2</code> model <code>forward_range</code>.
              In that case, <code>iterator_category</code> denotes <code>forward_iterator_tag</code> if
              <i>qualified-id</i>
              <code>iterator_traits&lt;iterator_t&lt;V1&gt;&gt;::iterator_category</code> and
              <code>iterator_traits&lt;iterator_t&lt;V2&gt;&gt;::iterator_category</code> denote a type
              that models
              <code>derived_from&lt;forward_iterator_tag&gt;</code>;
              otherwise it denotes <code>input_iterator_tag</code>.
            </p>
          </blockquote>
          <pre>constexpr void <i>satisfy</i>();</pre>
          <blockquote>
            <p>-2- <i>Effects</i>: Equivalent to:</p>
            <pre>
while (true) {
  if (<i>current1_</i> == <i>end1_</i>)
    return;
  if (<i>current2_</i> == <i>end2_</i>)
    return;
  if (*<i>current1_</i> &lt; *<i>current2_</i>)
    return;
  if (*<i>current2_</i> &lt; *<i>current1_</i>)
    ++<i>current2_</i>;
  else {
    ++<i>current1_</i>;
    ++<i>current2_</i>;
  }
}
</pre>
            [<i>Note 1</i>: <code>set_difference_view</code> iterators use the <code><i>satisfy</i></code> function to
            find the next valid element in the first range.— <i>end note</i>]
          </blockquote>
          <pre>constexpr <i>iterator</i>(iterator_t&lt;V1&gt; current1, sentinel_t&lt;V1&gt; end1,
                   iterator_t&lt;V2&gt; current2, sentinel_t&lt;V2&gt; end2);</pre>
          <blockquote>
            <p>-3- <i>Effects</i>: Initializes <code><i>current1_</i></code> with
              <code>std::move(current1)</code>,
              <code><i>end1_</i></code> with <code>end1</code>,
              <code><i>current2_</i></code> with <code>std::move(current2)</code>,
              <code><i>end2_</i></code> with <code>end2</code>; then calls <code><i>satisfy</i>()</code>.
            </p>
          </blockquote>
          <pre>constexpr <i>iterator</i>& operator++();</pre>
          <blockquote>
            <p>-4- <i>Effects</i>: Equivalent to:</p>
            <pre>
++<i>current1_</i>;
<i>satisfy</i>();
return *this;
</pre>
          </blockquote>
          <pre>constexpr void operator++(int);</pre>
          <blockquote>
            <p>-5- <i>Effects</i>: Equivalent to <code>++*this</code>.</p>
          </blockquote>
          <pre>constexpr <i>iterator</i> operator++(int) requires forward_range&lt;V1&gt; && forward_range&lt;V2&gt;;</pre>
          <blockquote>
            <p>-6- <i>Effects</i>: Equivalent to:</p>
            <pre>
auto tmp = *this;
++*this;
return tmp;
</pre>
          </blockquote>
          <pre>friend constexpr bool operator==(const <i>iterator</i>& x, const <i>iterator</i>& y)
  requires equality_comparable&lt;iterator_t&lt;V1&gt;&gt;;</pre>
          <blockquote>
            <p>-7- <i>Effects</i>: Equivalent to: <code>return x.<i>current1_</i> == y.<i>current1_</i>;</code>
          </blockquote>
          <pre>friend constexpr bool operator==(const <i>iterator</i>& x, default_sentinel_t);</pre>
          <blockquote>
            <p>-8- <i>Effects</i>: Equivalent to: <code>return x.<i>current1_</i> == x.<i>end1_</i>;</code>
          </blockquote>
          <pre>friend constexpr decltype(auto) iter_move(const <i>iterator</i>& i)
  noexcept(noexcept(ranges::iter_move(i.<i>current1_</i>)));</pre>
          <blockquote>
            <p>-9- <i>Effects</i>: Equivalent to: <code>return ranges::iter_move(i.<i>current1</i>_);</code>
          </blockquote>
        </li>
        <li>
          <p>Add <b>25.7.? Set intersection view [range.set.intersection]</b></a> after [range.set.difference] as
            indicated:</p>
          <p>[25.7.?.1] Overview [range.set.intersection.overview]</p>
          <p>
            -1- <code>set_intersection_view</code> presents a view of set intersection between two sorted ranges.
          </p>
          <p>
            -2- The name <code>views::set_intersection</code> denotes a range adaptor object (25.7.2 <a
              href="https://eel.is/c++draft/range.adaptor.object">[range.adaptor.object]</a>).
            Given subexpressions <code>E</code> and F, the expression <code>views::set_intersection(E, F)</code> is
            expression-equivalent to <code>set_intersection_view(E, F)</code>.
          </p>
          <p>-3- [<i>Example 1</i>:</p>
          <pre>
vector v1{1, 2, 2, 3,    4, 5, 6};
vector v2{   2, 2, 3, 3,    5,    7};
println("{}", views::set_intersection(v1, v2)); // <i>prints</i> [2 2 3 5]</pre>
          — <i>end example]</i>
          <p>[25.7.?.2] Class template <code>set_intersection_view</code> [range.set.intersection.view]</p>
          <pre>
namespace std::ranges {
  template&lt;view V1, view V2&gt;
    requires <i>set-operable</i>&lt;V1, V2&gt;
  class set_intersection_view : public view_interface&lt;set_intersection_view&lt;V1, V2&gt;&gt; {
    V1 <i>base1_</i> = V1();        // <i>exposition only</i>
    V2 <i>base2_</i> = V2();        // <i>exposition only</i>

    // <i>[range.set.intersection.iterator], class</i> set_intersection_view::<i>iterator</i>
    class <i>iterator</i>;          // <i>exposition only</i>

  public:
    set_intersection_view()
      requires default_initializable&lt;V1&gt; && default_initializable&lt;V2&gt; = default;

    constexpr explicit set_intersection_view(V1 base1, V2 base2);

    constexpr V1 base() const & requires copy_constructible&lt;V1&gt; { return <i>base1_</i>; }
    constexpr V1 base() && { return std::move(<i>base1_</i>); }

    constexpr <i>iterator</i> begin();

    constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
  };

  template&lt;class R1, class R2&gt;
    set_intersection_view(R1&amp;&amp;, R2&amp;&amp;)
      -&gt; set_intersection_view&lt;views::all_t&lt;R1&gt;, views::all_t&lt;R2&gt;&gt;;
}
</pre>
          <pre>constexpr explicit set_intersection_view(V1 base1, V2 base2);</pre>
          <blockquote>
            <p>-1- <i>Effects</i>: Initializes <code><i>base1_</i></code> with <code>std::move(base1)</code> and
              <code><i>base2_</i></code> with <code>std::move(base2)</code>.
              The behavior is undefined if either <code>ranges::is_sorted(<i>base1_</i>)</code> or
              <code>ranges::is_sorted(<i>base2_</i>)</code> is <code>false</code>.
            </p>
          </blockquote>
          <pre>constexpr iterator begin();</pre>
          <blockquote>
            <p>-2- <i>Returns</i>: <code>{ranges::begin(<i>base1_</i>), ranges::end(<i>base1_</i>), ranges::begin(<i>base2_</i>),
ranges::end(<i>base2_</i>)}</code>.
            </p>
            <p>-3- <i>Remarks</i>: In order to provide the amortized constant time complexity required by the
              <code>range</code>
              concept when <code>set_intersection_view</code> models <code>forward_range</code>, this function caches
              the
              result within the <code>set_intersection_view</code> for
              use on subsequent calls.
            </p>
          </blockquote>
          <p>[25.7.?.2] Class <code>set_intersection_view::<i>iterator</i></code> [range.set.intersection.iterator]</p>
          <pre>
namespace std::ranges {
  template&lt;view V1, view V2&gt;
    requires <i>set-operable</i>&lt;V1, V2&gt;
  class set_intersection_view&lt;V1, V2&gt;&gt;::<i>iterator</i> {
    iterator_t&lt;V1&gt; <i>current1_</i> = iterator_t&lt;V1&gt;();    // <i>exposition only</i>
    sentinel_t&lt;V1&gt; <i>end1_</i>     = sentinel_t&lt;V1&gt;();    // <i>exposition only</i>
    iterator_t&lt;V2&gt; <i>current2_</i> = iterator_t&lt;V2&gt;();    // <i>exposition only</i>
    sentinel_t&lt;V2&gt; <i>end2_</i>     = sentinel_t&lt;V2&gt;();    // <i>exposition only</i>

    constexpr void <i>satisfy</i>();                       // <i>exposition only</i>

    constexpr <i>iterator</i>(iterator_t&lt;V1&gt; current1, sentinel_t&lt;V1&gt; end1,       // <i>exposition only</i>
                       iterator_t&lt;V2&gt; current2, sentinel_t&lt;V2&gt; end2);

  public:
    using iterator_category = <i>see below</i>;                        // <i>not always present</i>
    using iterator_concept  =
      conditional_t&lt;forward_range&lt;V1&gt; && forward_range&lt;V2&gt;, forward_iterator_tag, input_iterator_tag&gt;;
    using value_type        = range_value_t&lt;V1&gt;;
    using difference_type   = range_difference_t&lt;V1&gt;;

    <i>iterator</i>()
      requires default_initializable&lt;iterator_t&lt;V1&gt;&gt; && default_initializable&lt;iterator_t&lt;V2&gt;&gt;
        = default;

    constexpr iterator_t&lt;V1&gt; base() && { return std::move(<i>current1_</i>); }
    constexpr const iterator_t&lt;V1&gt;& base() const & noexcept { return <i>current1_</i>; }

    constexpr decltype(auto) operator*() const { return *<i>current1_</i>; }

    constexpr <i>iterator</i>& operator++();
    constexpr void operator++(int);
    constexpr <i>iterator</i> operator++(int) requires forward_range&lt;V1&gt; && forward_range&lt;V2&gt;;

    friend constexpr bool operator==(const <i>iterator</i>& x, const <i>iterator</i>& y)
      requires equality_comparable&lt;iterator_t&lt;V1&gt;&gt;;
    friend constexpr bool operator==(const <i>iterator</i>& x, default_sentinel_t);

    friend constexpr decltype(auto) iter_move(const <i>iterator</i>& i)
      noexcept(noexcept(ranges::iter_move(i.<i>current1_</i>)));
  };
}
</pre>
          <blockquote>
            <p>-1- The member <i>typedef-name</i> <code>iterator_category</code> is defined if and only if
              <code>V1</code> and <code>V2</code> model <code>forward_range</code>.
              In that case, <code>iterator_category</code> denotes <code>forward_iterator_tag</code> if
              <i>qualified-id</i>
              <code>iterator_traits&lt;iterator_t&lt;V1&gt;&gt;::iterator_category</code> and
              <code>iterator_traits&lt;iterator_t&lt;V2&gt;&gt;::iterator_category</code> denote a type
              that models
              <code>derived_from&lt;forward_iterator_tag&gt;</code>;
              otherwise it denotes <code>input_iterator_tag</code>.
            </p>
          </blockquote>
          <pre>constexpr void <i>satisfy</i>();</pre>
          <blockquote>
            <p>-2- <i>Effects</i>: Equivalent to:</p>
            <pre>
while (true) {
  if (<i>current1_</i> == <i>end1_</i>)
    return;
  if (<i>current2_</i> == <i>end2_</i>)
    return;
  if (*<i>current1_</i> &lt; *<i>current2_</i>)
    ++<i>current1_</i>;
  else if (*<i>current2_</i> &lt; *<i>current1_</i>)
    ++<i>current2_</i>;
  else
    return;
}
</pre>
            [<i>Note 1</i>: <code>set_intersection_view</code> iterators use the <code><i>satisfy</i></code> function to
            find the next valid element in the first range.— <i>end note</i>]
          </blockquote>
          <pre>constexpr <i>iterator</i>(iterator_t&lt;V1&gt; current1, sentinel_t&lt;V1&gt; end1,
                   iterator_t&lt;V2&gt; current2, sentinel_t&lt;V2&gt; end2);</pre>
          <blockquote>
            <p>-3- <i>Effects</i>: Initializes <code><i>current1_</i></code> with
              <code>std::move(current1)</code>,
              <code><i>end1_</i></code> with <code>end1</code>,
              <code><i>current2_</i></code> with <code>std::move(current2)</code>,
              <code><i>end2_</i></code> with <code>end2</code>; then calls <code><i>satisfy</i>()</code>.
            </p>
          </blockquote>
          <pre>constexpr <i>iterator</i>& operator++();</pre>
          <blockquote>
            <p>-4- <i>Effects</i>: Equivalent to:</p>
            <pre>
++<i>current1_</i>;
++<i>current2_</i>;
<i>satisfy</i>();
return *this;
</pre>
          </blockquote>
          <pre>constexpr void operator++(int);</pre>
          <blockquote>
            <p>-5- <i>Effects</i>: Equivalent to <code>++*this</code>.</p>
          </blockquote>
          <pre>constexpr <i>iterator</i> operator++(int) requires forward_range&lt;V1&gt; && forward_range&lt;V2&gt;;</pre>
          <blockquote>
            <p>-6- <i>Effects</i>: Equivalent to:</p>
            <pre>
auto tmp = *this;
++*this;
return tmp;
</pre>
          </blockquote>
          <pre>friend constexpr bool operator==(const <i>iterator</i>& x, const <i>iterator</i>& y)
  requires equality_comparable&lt;iterator_t&lt;V1&gt;&gt;;</pre>
          <blockquote>
            <p>-7- <i>Effects</i>: Equivalent to: <code>return x.<i>current1_</i> == y.<i>current1_</i>;</code>
          </blockquote>
          <pre>friend constexpr bool operator==(const <i>iterator</i>& x, default_sentinel_t);</pre>
          <blockquote>
            <p>-8- <i>Effects</i>: Equivalent to:
              <code>return x.<i>current1_</i> == x.<i>end1_</i> || x.<i>current2_</i> == x.<i>end2_</i>;</code>
          </blockquote>
          <pre>friend constexpr decltype(auto) iter_move(const <i>iterator</i>& i)
  noexcept(noexcept(ranges::iter_move(i.<i>current1_</i>)));</pre>
          <blockquote>
            <p>-9- <i>Effects</i>: Equivalent to: <code>return ranges::iter_move(i.<i>current1</i>_);</code>
          </blockquote>
        </li>
        <li>
          <p>Add <b>25.7.? Set union view [range.set.union]</b></a> after [range.set.intersection] as indicated:</p>
          <p>[25.7.?.1] Overview [range.set.union.overview]</p>
          <p>
            -1- <code>set_union_view</code> presents a view of set union between two sorted ranges.
          </p>
          <p>
            -2- The name <code>views::set_union</code> denotes a range adaptor object (25.7.2 <a
              href="https://eel.is/c++draft/range.adaptor.object">[range.adaptor.object]</a>).
            Given subexpressions <code>E</code> and F, the expression <code>views::set_union(E, F)</code> is
            expression-equivalent to <code>set_union_view(E, F)</code>.
          </p>
          <p>-3- [<i>Example 1</i>:</p>
          <pre>
vector v1{1, 2, 3, 4, 5};
vector v2{      3, 4, 5, 6, 7};
println("{}", views::set_union(v1, v2)); // <i>prints</i> [1 2 3 4 5 6 7]</pre>
          — <i>end example]</i>
          <p>[25.7.?.2] Class template <code>set_union_view</code> [range.set.union.view]</p>
          <pre>
namespace std::ranges {
  template&lt;class R1, class R2&gt;
    concept <i>set-operable-concatable</i> =          // <i>exposition only</i>
      <i>set-operable</i>&lt;R1, R2&gt; && <i>concatable</i>&lt;R1, R2&gt;;

  template&lt;view V1, view V2&gt;
    requires <i>set-operable-concatable</i>&lt;V1, V2&gt;
  class set_union_view : public view_interface&lt;set_union_view&lt;V1, V2&gt;&gt; {
    V1 <i>base1_</i> = V1();        // <i>exposition only</i>
    V2 <i>base2_</i> = V2();        // <i>exposition only</i>

    // <i>[range.set.union.iterator], class</i> template set_union_view::<i>iterator</i>
    template&lt;bool Const&gt;
    class <i>iterator</i>;          // <i>exposition only</i>

  public:
    set_union_view()
      requires default_initializable&lt;V1&gt; && default_initializable&lt;V2&gt; = default;

    constexpr explicit set_union_view(V1 base1, V2 base2);

    constexpr auto begin() requires (!<i>simple-view</i>&lt;V1&gt; || !<i>simple-view</i>&lt;V2&gt;) {
      return <i>iterator</i>&lt;false&gt;(ranges::begin(<i>base1_</i>), ranges::end(<i>base1_</i>), 
                             ranges::begin(<i>base2_</i>), ranges::end(<i>base2_</i>));
    }

    constexpr auto begin() const requires <i>set-operable-concatable</i>&lt;V1, V2&gt; {
      return <i>iterator</i>&lt;true&gt;(ranges::begin(<i>base1_</i>), ranges::end(<i>base1_</i>), 
                            ranges::begin(<i>base2_</i>), ranges::end(<i>base2_</i>));
    }

    constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
  };
  
  template&lt;class R1, class R2&gt;
    set_union_view(R1&amp;&amp;, R2&amp;&amp;)
      -&gt; set_union_view&lt;views::all_t&lt;R1&gt;, views::all_t&lt;R2&gt;&gt;;
}
</pre>
          <pre>constexpr explicit set_union_view(V1 base1, V2 base2);</pre>
          <blockquote>
            <p>-1- <i>Effects</i>: Initializes <code><i>base1_</i></code> with <code>std::move(base1)</code> and
              <code><i>base2_</i></code> with <code>std::move(base2)</code>.
              The behavior is undefined if either <code>ranges::is_sorted(<i>base1_</i>)</code> or
              <code>ranges::is_sorted(<i>base2_</i>)</code> is <code>false</code>.
            </p>
          </blockquote>

          <p>[25.7.?.2] Class template <code>set_union_view::<i>iterator</i></code> [range.set.union.iterator]</p>
          <pre>
namespace std::ranges {
  template&lt;view V1, view V2&gt;
    requires <i>set-operable-concatable</i>&lt;V1, V2&gt;
  template&lt;bool Const&gt;
  class set_union_view&lt;V1, V2&gt;&gt;::<i>iterator</i> {
    using <i>Base1</i> = <i>maybe-const</i>&lt;Const, V1&gt;;                 // <i>exposition only</i>
    using <i>Base2</i> = <i>maybe-const</i>&lt;Const, V2&gt;;                 // <i>exposition only</i>
    iterator_t&lt;<i>Base1</i>&gt; <i>current1_</i> = iterator_t&lt;<i>Base1</i>&gt;();    // <i>exposition only</i>
    sentinel_t&lt;<i>Base1</i>&gt; <i>end1_</i>     = sentinel_t&lt;<i>Base1</i>&gt;();    // <i>exposition only</i>
    iterator_t&lt;<i>Base2</i>&gt; <i>current2_</i> = iterator_t&lt;<i>Base2</i>&gt;();    // <i>exposition only</i>
    sentinel_t&lt;<i>Base2</i>&gt; <i>end2_</i>     = sentinel_t&lt;<i>Base2</i>&gt;();    // <i>exposition only</i>
    bool <i>use_first_</i> = false;                              // <i>exposition only</i>

    constexpr void <i>satisfy</i>();                             // <i>exposition only</i>

    constexpr <i>iterator</i>(iterator_t&lt;<i>Base1</i>&gt; current1, sentinel_t&lt;<i>Base1</i>&gt; end1,       // <i>exposition only</i>
                       iterator_t&lt;<i>Base2</i>&gt; current2, sentinel_t&lt;<i>Base2</i>&gt; end2);

  public:
    using iterator_category = <i>see below</i>;                        // <i>not always present</i>
    using iterator_concept  =
      conditional_t&lt;forward_range&lt;<i>Base1</i>&gt; && forward_range&lt;<i>Base2</i>&gt;, forward_iterator_tag, input_iterator_tag&gt;;
    using value_type        = <i>concat-value-t</i>&lt;V1, V2&gt;;
    using difference_type   = common_type_t&lt;range_difference_t&lt;<i>Base1</i>&gt;, range_difference_t&lt;<i>Base2</i>&gt;&gt;;

    <i>iterator</i>()
      requires default_initializable&lt;iterator_t&lt;<i>Base1</i>&gt;&gt; && default_initializable&lt;iterator_t&lt;<i>Base2</i>&gt;&gt;
        = default;

    constexpr <i>iterator</i>(<i>iterator</i>&lt;!Const&gt; i)
      requires Const && convertible_to&lt;iterator_t&lt;V1&gt;, iterator_t&lt;<i>Base1</i>&gt;&gt; &&
                        convertible_to&lt;sentinel_t&lt;V1&gt;, sentinel_t&lt;<i>Base1</i>&gt;&gt; &&
                        convertible_to&lt;iterator_t&lt;V2&gt;, iterator_t&lt;<i>Base2</i>&gt;&gt; &&
                        convertible_to&lt;sentinel_t&lt;V2&gt;, sentinel_t&lt;<i>Base2</i>&gt;&gt;;

    constexpr <i>concat-reference-t</i>&lt;<i>Base1</i>, <i>Base2</i>&gt; operator*() const;

    constexpr <i>iterator</i>& operator++();
    constexpr void operator++(int);
    constexpr <i>iterator</i> operator++(int) requires forward_range&lt;<i>Base1</i>&gt; && forward_range&lt;<i>Base2</i>&gt;;

    friend constexpr bool operator==(const <i>iterator</i>& x, const <i>iterator</i>& y)
      requires equality_comparable&lt;iterator_t&lt;<i>Base1</i>&gt;&gt && equality_comparable&lt;iterator_t&lt;<i>Base2</i>&gt;&gt;;
    friend constexpr bool operator==(const <i>iterator</i>& x, default_sentinel_t);

    friend constexpr <i>concat-rvalue-reference-t</i>&lt;<i>Base1</i>, <i>Base2</i>&gt; iter_move(const <i>iterator</i>& i) noexcept(<i>see below</i>);
  };
}</pre>
          <blockquote>
            <p>-1- The member <i>typedef-name</i> <code>iterator_category</code> is defined if and only if
              <code><i>Base1</i></code> and <code><i>Base2</i></code> model <code>forward_range</code>.
              In that case, <code>iterator_category</code> denotes <code>forward_iterator_tag</code> if
              <i>qualified-id</i>
              <code>iterator_traits&lt;iterator_t&lt;<i>Base1</i>&gt;&gt;::iterator_category</code> and
              <code>iterator_traits&lt;iterator_t&lt;<i>Base2</i>&gt;&gt;::iterator_category</code> denote a type
              that models
              <code>derived_from&lt;forward_iterator_tag&gt;</code>;
              otherwise it denotes <code>input_iterator_tag</code>.
            </p>
          </blockquote>
          <pre>constexpr void <i>satisfy</i>();</pre>
          <blockquote>
            <p>-2- <i>Effects</i>: Equivalent to:</p>
            <pre>
if (<i>current1_</i> == <i>end1_</i>) {
  <i>use_first_</i> = false;
  return;
}
if (<i>current2_</i> == <i>end2_</i>) {
  <i>use_first_</i> = true;
  return;
}
if (*<i>current1_</i> &lt; *<i>current2_</i>) {
  <i>use_first_</i> = true;
  return;
}
if (*<i>current2_</i> &lt; *<i>current1_</i>) {
  <i>use_first_</i> = false;
  return;
}
<i>use_first_</i> = true;
++<i>current2_</i>;
                  </pre>
            [<i>Note 1</i>: <code>set_union_view</code> iterators use the <code><i>satisfy</i></code>
            function to
            find the next valid element in two ranges.— <i>end note</i>]
          </blockquote>
          <pre>constexpr <i>iterator</i>(iterator_t&lt;<i>Base1</i>&gt; current1, sentinel_t&lt;<i>Base1</i>&gt; end1,
                   iterator_t&lt;<i>Base2</i>&gt; current2, sentinel_t&lt;<i>Base2</i>&gt; end2);</pre>
          <blockquote>
            <p>-3- <i>Effects</i>: Initializes <code><i>current1_</i></code> with
              <code>std::move(current1)</code>,
              <code><i>end1_</i></code> with <code>end1</code>,
              <code><i>current2_</i></code> with <code>std::move(current2)</code>,
              <code><i>end2_</i></code> with <code>end2</code>; then calls <code><i>satisfy</i>()</code>.
            </p>
          </blockquote>
          <pre>constexpr <i>iterator</i>(<i>iterator</i>&lt;!Const&gt; i)
  requires Const && convertible_to&lt;iterator_t&lt;V1&gt;, iterator_t&lt;<i>Base1</i>&gt;&gt; &&
                    convertible_to&lt;sentinel_t&lt;V1&gt;, sentinel_t&lt;<i>Base1</i>&gt;&gt; &&
                    convertible_to&lt;iterator_t&lt;V2&gt;, iterator_t&lt;<i>Base2</i>&gt;&gt; &&
                    convertible_to&lt;sentinel_t&lt;V2&gt;, sentinel_t&lt;<i>Base2</i>&gt;&gt;;</pre>
          <blockquote>
            <p>-4- <i>Effects</i>: Initializes <code><i>current1_</i></code> with
              <code>std::move(i.<i>current1_</i>)</code>,
              <code><i>end1_</i></code> with <code>i.<i>end1_</i></code>,
              <code><i>current2_</i></code> with <code>std::move(i.<i>current2_</i>)</code>,
              <code><i>end2_</i></code> with <code>i.<i>end2_</i></code>, and <code><i>use_first_</i></code> with
              <code>i.<i>use_first_</i></code>.
            </p>
          </blockquote>
          <pre>constexpr <i>concat-reference-t</i>&lt;<i>Base1</i>, <i>Base2</i>&gt; operator*() const;</pre>
          <blockquote>
            <p>-5- <i>Effects</i>: Equivalent to:</p>
            <pre>
if (<i>use_first_</i>)
  return *<i>current1_</i>;
return *<i>current2_</i>;</pre>
          </blockquote>
          <pre>constexpr <i>iterator</i>& operator++();</pre>
          <blockquote>
            <p>-6- <i>Effects</i>: Equivalent to:</p>
            <pre>
if (<i>use_first_</i>)
  ++<i>current1_</i>;
else
  ++<i>current2_</i>;
<i>satisfy</i>();
return *this;</pre>
          </blockquote>
          <pre>constexpr void operator++(int);</pre>
          <blockquote>
            <p>-7- <i>Effects</i>: Equivalent to <code>++*this</code>.</p>
          </blockquote>
          <pre>constexpr <i>iterator</i> operator++(int) requires forward_range&lt;<i>Base1</i>&gt; && forward_range&lt;<i>Base2</i>&gt;;</pre>
          <blockquote>
            <p>-8- <i>Effects</i>: Equivalent to:</p>
            <pre>
auto tmp = *this;
++*this;
return tmp;
                  </pre>
          </blockquote>
          <pre>friend constexpr bool operator==(const <i>iterator</i>& x, const <i>iterator</i>& y)
  requires equality_comparable&lt;iterator_t&lt;<i>Base1</i>&gt;&gt; && equality_comparable&lt;iterator_t&lt;<i>Base2</i>&gt;&gt;;</pre>
          <blockquote>
            <p>-9- <i>Effects</i>: Equivalent to:
              <code>return x.<i>current1_</i> == y.<i>current1_</i> && x.<i>current2_</i> == y.<i>current2_</i>;</code>
          </blockquote>
          <pre>friend constexpr bool operator==(const <i>iterator</i>& x, default_sentinel_t);</pre>
          <blockquote>
            <p>-10- <i>Effects</i>: Equivalent to:
              <code>return x.<i>current1_</i> == x.<i>end1_</i> && x.<i>current2_</i> == x.<i>end2_</i>;</code>
          </blockquote>
          <pre>friend constexpr <i>concat-rvalue-reference-t</i>&lt;<i>Base1</i>, <i>Base2</i>&gt; iter_move(const <i>iterator</i>& i) noexcept(<i>see below</i>);</pre>
          <blockquote>
            <p>-11- <i>Effects</i>: Equivalent to:
            <pre>
if (i.<i>use_first_</i>)
  return ranges::iter_move(i.<i>current1_</i>);
return ranges::iter_move(i.<i>current2_</i>);</pre>
            <p>-12- <i>Remarks</i>: The exception specification is equivalent to:</p>
            <pre>
noexcept(ranges::iter_move(i.<i>current1_</i>)) &&
noexcept(ranges::iter_move(i.<i>current2_</i>)) &&
is_nothrow_convertible_v&lt;range_rvalue_reference_t&lt;<i>Base1</i>&gt;, <i>concat-rvalue-reference-t</i>&lt;<i>Base1</i>, <i>Base2</i>&gt;&gt; && 
is_nothrow_convertible_v&lt;range_rvalue_reference_t&lt;<i>Base2</i>&gt;, <i>concat-rvalue-reference-t</i>&lt;<i>Base1</i>, <i>Base2</i>&gt;&gt;</pre>
          </blockquote>
        </li>
        <p>Add <b>25.7.? Set symmetric difference view [range.set.symmetric.difference]</b></a> after
          [range.set.union] as indicated:</p>
        <p>[25.7.?.1] Overview [range.set.symmetric.difference.overview]</p>
        <p>
          -1- <code>set_symmetric_difference_view</code> presents a view of set symmetric difference between two
          sorted ranges.
        </p>
        <p>
          -2- The name <code>views::set_symmetric_difference</code> denotes a range adaptor object (25.7.2 <a
            href="https://eel.is/c++draft/range.adaptor.object">[range.adaptor.object]</a>).
          Given subexpressions <code>E</code> and F, the expression <code>views::set_symmetric_difference(E, F)</code>
          is
          expression-equivalent to <code>set_symmetric_difference_view(E, F)</code>.
        </p>
        <p>-3- [<i>Example 1</i>:</p>
        <pre>
vector v1{1, 3, 4,    6, 7, 9};
vector v2{1,    4, 5, 6,    9};
println("{}", views::set_symmetric_difference(v1, v2)); // <i>prints</i> [3 5 7]</pre>
        — <i>end example]</i>
        <p>[25.7.?.2] Class template <code>set_symmetric_difference_view</code> [range.set.symmetric.difference.view]
        </p>
        <pre>
namespace std::ranges {
  template&lt;view V1, view V2&gt;
    requires <i>set-operable-concatable</i>&lt;V1, V2&gt;
  class set_symmetric_difference_view : public view_interface&lt;set_symmetric_difference_view&lt;V1, V2&gt;&gt; {
    V1 <i>base1_</i> = V1();        // <i>exposition only</i>
    V2 <i>base2_</i> = V2();        // <i>exposition only</i>

    // <i>[range.set.symmetric.difference.iterator], class</i> set_symmetric_difference_view::<i>iterator</i>
    class <i>iterator</i>;          // <i>exposition only</i>

  public:
    set_symmetric_difference_view()
      requires default_initializable&lt;V1&gt; && default_initializable&lt;V2&gt; = default;

    constexpr explicit set_symmetric_difference_view(V1 base1, V2 base2);

    constexpr <i>iterator</i> begin();

    constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
  };

  template&lt;class R1, class R2&gt;
    set_symmetric_difference_view(R1&amp;&amp;, R2&amp;&amp;)
      -&gt; set_symmetric_difference_view&lt;views::all_t&lt;R1&gt;, views::all_t&lt;R2&gt;&gt;;
}
</pre>
        <pre>constexpr explicit set_symmetric_difference_view(V1 base1, V2 base2);</pre>
        <blockquote>
          <p>-1- <i>Effects</i>: Initializes <code><i>base1_</i></code> with <code>std::move(base1)</code> and
            <code><i>base2_</i></code> with <code>std::move(base2)</code>.
            The behavior is undefined if either <code>ranges::is_sorted(<i>base1_</i>)</code> or
            <code>ranges::is_sorted(<i>base2_</i>)</code> is <code>false</code>.
          </p>
        </blockquote>
        <pre>constexpr <i>iterator</i> begin();</pre>
        <blockquote>
          <p>-2- <i>Returns</i>: <code>{ranges::begin(<i>base1_</i>), ranges::end(<i>base1_</i>), ranges::begin(<i>base2_</i>),
ranges::end(<i>base2_</i>)}</code>.
          </p>
          <p>-3- <i>Remarks</i>: In order to provide the amortized constant time complexity required by the
            <code>range</code>
            concept when <code>set_symmetric_difference_view</code> models <code>forward_range</code>, this function
            caches
            the
            result within the <code>set_symmetric_difference_view</code> for
            use on subsequent calls.
          </p>
        </blockquote>
        <p>[25.7.?.2] Class <code>set_symmetric_difference_view::<i>iterator</i></code>
          [range.set.symmetric.difference.iterator]</p>
        <pre>
namespace std::ranges {
  template&lt;view V1, view V2&gt;
    requires <i>set-operable-concatable</i>&lt;V1, V2&gt;
  class set_symmetric_difference_view&lt;V1, V2&gt;&gt;::<i>iterator</i> {
    iterator_t&lt;V1&gt; <i>current1_</i> = iterator_t&lt;V1&gt;();    // <i>exposition only</i>
    sentinel_t&lt;V1&gt; <i>end1_</i>     = sentinel_t&lt;V1&gt;();    // <i>exposition only</i>
    iterator_t&lt;V2&gt; <i>current2_</i> = iterator_t&lt;V2&gt;();    // <i>exposition only</i>
    sentinel_t&lt;V2&gt; <i>end2_</i>     = sentinel_t&lt;V2&gt;();    // <i>exposition only</i>
    bool <i>use_first_</i> = false;                        // <i>exposition only</i>

    constexpr void <i>satisfy</i>();                       // <i>exposition only</i>

    constexpr <i>iterator</i>(iterator_t&lt;V1&gt; current1, sentinel_t&lt;V1&gt; end1,       // <i>exposition only</i>
                       iterator_t&lt;V2&gt; current2, sentinel_t&lt;V2&gt; end2);

  public:
    using iterator_category = <i>see below</i>;                        // <i>not always present</i>
    using iterator_concept  =
      conditional_t&lt;forward_range&lt;V1&gt; && forward_range&lt;V2&gt;, forward_iterator_tag, input_iterator_tag&gt;;
    using value_type        = <i>concat-value-t</i>&lt;V1, V2&gt;;
    using difference_type   = common_type_t&lt;range_difference_t&lt;V1&gt;, range_difference_t&lt;V2&gt;&gt;;

    <i>iterator</i>()
      requires default_initializable&lt;iterator_t&lt;V1&gt;&gt; && default_initializable&lt;iterator_t&lt;V2&gt;&gt;
        = default;

    constexpr <i>concat-reference-t</i>&lt;V1, V2&gt; operator*() const;

    constexpr <i>iterator</i>& operator++();
    constexpr void operator++(int);
    constexpr <i>iterator</i> operator++(int) requires forward_range&lt;V1&gt; && forward_range&lt;V2&gt;;

    friend constexpr bool operator==(const <i>iterator</i>& x, const <i>iterator</i>& y)
      requires equality_comparable&lt;iterator_t&lt;V1&gt;&gt; && equality_comparable&lt;iterator_t&lt;V2&gt;&gt;;
    friend constexpr bool operator==(const <i>iterator</i>& x, default_sentinel_t);

    friend constexpr <i>concat-rvalue-reference-t</i>&lt;V1, V2&gt; iter_move(const <i>iterator</i>& i) noexcept(<i>see below</i>);
  };
}</pre>
        <blockquote>
          <p>-1- The member <i>typedef-name</i> <code>iterator_category</code> is defined if and only if
            <code>V1</code> and <code>V2</code> model <code>forward_range</code>.
            In that case, <code>iterator_category</code> denotes <code>forward_iterator_tag</code> if
            <i>qualified-id</i>
            <code>iterator_traits&lt;iterator_t&lt;V1&gt;&gt;::iterator_category</code> and
            <code>iterator_traits&lt;iterator_t&lt;V2&gt;&gt;::iterator_category</code> denote a type
            that models
            <code>derived_from&lt;forward_iterator_tag&gt;</code>;
            otherwise it denotes <code>input_iterator_tag</code>.
          </p>
        </blockquote>
        <pre>constexpr void <i>satisfy</i>();</pre>
        <blockquote>
          <p>-2- <i>Effects</i>: Equivalent to:</p>
          <pre>
while (true) {
  if (<i>current1_</i> == <i>end1_</i>) {
    <i>use_first_</i> = false;
    return;
  }
  if (<i>current2_</i> == <i>end2_</i>)  {
    <i>use_first_</i> = true;
    return;
  }
  if (*<i>current1_</i> &lt; *<i>current2_</i>) {
    <i>use_first_</i> = true;
    return;
  }
  if (*<i>current2_</i> &lt; *<i>current1_</i>) {
    <i>use_first_</i> = false;
    return;
  }
  ++<i>current1_</i>;
  ++<i>current2_</i>;
}
</pre>[<i>Note 1</i>: <code>set_symmetric_difference_view</code> iterators use the <code><i>satisfy</i></code>
          function to
          find the next valid element in two ranges.— <i>end note</i>]
        </blockquote>
        <pre>constexpr <i>iterator</i>(iterator_t&lt;V1&gt; current1, sentinel_t&lt;V1&gt; end1,
                   iterator_t&lt;V2&gt; current2, sentinel_t&lt;V2&gt; end2);</pre>
        <blockquote>
          <p>-3- <i>Effects</i>: Initializes <code><i>current1_</i></code> with
            <code>std::move(current1)</code>,
            <code><i>end1_</i></code> with <code>end1</code>,
            <code><i>current2_</i></code> with <code>std::move(current2)</code>,
            <code><i>end2_</i></code> with <code>end2</code>; then calls <code><i>satisfy</i>()</code>.
          </p>
        </blockquote>
        <pre>constexpr <i>concat-reference-t</i>&lt;V1, V2&gt; operator*() const;</pre>
        <blockquote>
          <p>-4- <i>Effects</i>: Equivalent to:</p>
          <pre>
if (<i>use_first_</i>)
  return *<i>current1_</i>;
return *<i>current2_</i>;
</pre>
        </blockquote>
        <pre>constexpr <i>iterator</i>& operator++();</pre>
        <blockquote>
          <p>-5- <i>Effects</i>: Equivalent to:</p>
          <pre>
if (<i>use_first_</i>)
  ++<i>current1_</i>;
else
  ++<i>current2_</i>;
<i>satisfy</i>();
return *this;
</pre>
        </blockquote>
        <pre>constexpr void operator++(int);</pre>
        <blockquote>
          <p>-6- <i>Effects</i>: Equivalent to <code>++*this</code>.</p>
        </blockquote>
        <pre>constexpr <i>iterator</i> operator++(int) requires forward_range&lt;V1&gt; && forward_range&lt;V2&gt;;</pre>
        <blockquote>
          <p>-7- <i>Effects</i>: Equivalent to:</p>
          <pre>
auto tmp = *this;
++*this;
return tmp;</pre>
        </blockquote>
        <pre>friend constexpr bool operator==(const <i>iterator</i>& x, const <i>iterator</i>& y)
  requires equality_comparable&lt;iterator_t&lt;V1&gt;&gt; && equality_comparable&lt;iterator_t&lt;V2&gt;&gt;;</pre>
        <blockquote>
          <p>-8- <i>Effects</i>: Equivalent to:
            <code>return x.<i>current1_</i> == y.<i>current1_</i> && x.<i>current2_</i> == y.<i>current2_</i>;</code>
        </blockquote>
        <pre>friend constexpr bool operator==(const <i>iterator</i>& x, default_sentinel_t);</pre>
        <blockquote>
          <p>-9- <i>Effects</i>: Equivalent to:
            <code>return x.<i>current1_</i> == x.<i>end1_</i> && x.<i>current2_</i> == x.<i>end2_</i>;</code>
        </blockquote>
        <pre>friend constexpr <i>concat-rvalue-reference-t</i>&lt;V1, V2&gt; iter_move(const <i>iterator</i>& i) noexcept(<i>see below</i>);</pre>
        <blockquote>
          <p>-10- <i>Effects</i>: Equivalent to:
          <pre>
if (i.<i>use_first_</i>)
  return ranges::iter_move(i.<i>current1_</i>);
return ranges::iter_move(i.<i>current2_</i>);</pre>
          <p>-11- <i>Remarks</i>: The exception specification is equivalent to:</p>
          <pre>
noexcept(ranges::iter_move(i.<i>current1_</i>)) &&
noexcept(ranges::iter_move(i.<i>current2_</i>)) &&
is_nothrow_convertible_v&lt;range_rvalue_reference_t&lt;V1&gt;, <i>concat-rvalue-reference-t</i>&lt;V1, V2&gt;&gt; && 
is_nothrow_convertible_v&lt;range_rvalue_reference_t&lt;V2&gt;, <i>concat-rvalue-reference-t</i>&lt;V1, V2&gt;&gt;
  </pre>
        </blockquote>
        </li>
      </ol>
  </div>
  <a name="References"></a>
  <h2>References</h2>
  <dd>
  <dt id="biblio-p2760">[P2760R1]
  <dd>Barry Revzin. A Plan for C++26 Ranges. URL: <a
      href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html</a>
  <dt>[range/v3]
  <dd>Eric Niebler. <code>views::set_<i>operations</i></code> implementation. URL: <a
      href="https://github.com/ericniebler/range-v3/blob/master/include/range/v3/view/set_algorithm.hpp">https://github.com/ericniebler/range-v3/blob/master/include/range/v3/view/set_algorithm.hpp</a>
</body>

</html>