Title
ranges::unique_copy's constraints for the case where result is an input_iterator are not quite right
Status
new
Section
[alg.unique]
Submitter
Hewill Kang

Created on 2024-05-14.00:00:00 last changed 1 month ago

Messages

Date: 2024-06-24.12:09:57

Proposed resolution:

This wording is relative to N4981.

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

    #include <initializer_list>     // see [initializer.list.syn]
    
    namespace std {
      […]
      namespace ranges {
        template<class I, class O>
          using unique_copy_result = in_out_result<I, O>;
    
        template<input_iterator I, sentinel_for<I> S, weakly_incrementable O, class Proj = identity,
                 indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to>
          requires indirectly_copyable<I, O> &&
                   (forward_iterator<I> ||
                    (input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>
                     indirect_equivalence_relation<C, projected<I, Proj>, projected<O, Proj>>) ||
                    indirectly_copyable_storable<I, O>)
          constexpr unique_copy_result<I, O>
            unique_copy(I first, S last, O result, C comp = {}, Proj proj = {});
        template<input_range R, weakly_incrementable O, class Proj = identity,
                 indirect_equivalence_relation<projected<iterator_t<R>, Proj>> C = ranges::equal_to>
          requires indirectly_copyable<iterator_t<R>, O> &&
                   (forward_iterator<iterator_t<R>> ||
                    (input_iterator<O> && same_as<range_value_t<R>, iter_value_t<O>>
                     indirect_equivalence_relation<C, projected<iterator_t<R>, Proj>,
                                                      projected<O, Proj>>) ||
                    indirectly_copyable_storable<iterator_t<R>, O>)
          constexpr unique_copy_result<borrowed_iterator_t<R>, O>
            unique_copy(R&& r, O result, C comp = {}, Proj proj = {});
      }
      […]
    }
    
  2. Modify [alg.unique] as indicated:

    template<input_iterator I, sentinel_for<I> S, weakly_incrementable O, class Proj = identity,
             indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to>
      requires indirectly_copyable<I, O> &&
               (forward_iterator<I> ||
                (input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>
                 indirect_equivalence_relation<C, projected<I, Proj>, projected<O, Proj>>) ||
                indirectly_copyable_storable<I, O>)
      constexpr ranges::unique_copy_result<I, O>
        ranges::unique_copy(I first, S last, O result, C comp = {}, Proj proj = {});
    template<input_range R, weakly_incrementable O, class Proj = identity,
             indirect_equivalence_relation<projected<iterator_t<R>, Proj>> C = ranges::equal_to>
      requires indirectly_copyable<iterator_t<R>, O> &&
               (forward_iterator<iterator_t<R>> ||
               (input_iterator<O> && same_as<range_value_t<R>, iter_value_t<O>>
                indirect_equivalence_relation<C, projected<iterator_t<R>, Proj>,
                                                 projected<O, Proj>>) ||
                indirectly_copyable_storable<iterator_t<R>, O>)
      constexpr ranges::unique_copy_result<borrowed_iterator_t<R>, O>
        ranges::unique_copy(R&& r, O result, C comp = {}, Proj proj = {});
    

    -6- Let pred be equal_to{} for the overloads in namespace std with no parameter pred, […]

Date: 2024-06-15.00:00:00

[ 2024-06-24; Reflector poll ]

Set priority to 3 after reflector poll.

Date: 2024-06-24.12:09:57

When r is only an input_range and result is also an input_iterator, ranges::unique_copy writes the elements of r into result and reads the value of result in the next iteration.

However, in this case, the function only requires that the value_type of r and the value_type of result are the same, which seems too loose because the value_type is not particularly useful in the ranges world compared to the reference, which is also reflected in the fact that the implementation applies the compare function on both dereferenced values (demo):

#include <algorithm>
#include <iostream>
#include <ranges>
#include <vector>

int main() {
  auto r = std::views::istream<bool>(std::cin);
  std::vector<bool> v(10);
  auto proj = [](std::same_as<bool> auto b) { return b; }; // ban vector<bool>::reference
  std::ranges::unique_copy(r, v.begin(), {}, proj);        // hard error in libstdc++, libc++ and MSVC-STL
}
History
Date User Action Args
2024-06-24 12:09:57adminsetmessages: + msg14192
2024-05-19 08:45:27adminsetmessages: + msg14147
2024-05-14 00:00:00admincreate