Search code examples
c++stliteratorc++20

Why ranges::unique_copy cannot work with std::ostream_iterator?


In [alg.unique], the signature of ranges::unique_copy is defined as:

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>>) ||
            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 = {});

But I found that when I is only an input_iterator and O is only an output_iterator, the following code fails to compile:

std::istringstream str("42 42 42");
std::ranges::unique_copy(
  std::istream_iterator<int>(str), 
  std::istream_iterator<int>(),
  std::ostream_iterator<int>(std::cout, " "));

Both gcc and msvc reject it with (godbolt):

error: no type named 'value_type' in 'using type = struct std::indirectly_readable_traits<std::ostream_iterator<int> >' {aka 'struct std::indirectly_readable_traits<std::ostream_iterator<int> >'}
 1436 |                            && same_as<iter_value_t<_Iter>, iter_value_t<_Out>>)

Surprisingly, the error message here does not show constraints not satisfied, it just complains that there is no value_type in indirectly_readable_traits<ostream_iterator<int>> when instantiating same_as<iter_value_t<_Iter>, iter_value_t<_Out>.

Why is the above code ill-formed in C++20?


Solution

  • This is a bug in both implementations. Both contain the equivalent of

    if constexpr (input_­iterator<O> && same_­as<iter_value_t<I>, iter_value_t<O>>) 
    

    In a constraint this is fine because constraint satisfaction is checked incrementally with short-circuiting (and in any event substitution failure just result in the constraint evaluating to false), so if O isn't an input iterator we won't ask about its (possibly non-existent) value type. In an if constexpr there is no short circuiting, so instead the whole expression is substituted into and is ill-formed on the spot.