C++20 ranges too many | operators?

TL;DR: In this case, the iterator type of the result of std::views::take is std::counted_iterator, which sometimes fails to model an iterator concept when it’s not expected to fail. This is LWG 3408 and is resolved by P2259.


This involves some really complicated mechanism.

Let

  • T be the iterator type of values | std::views::filter(even) | std::views::transform(square),
  • R be std::reverse_iterator<T>.

In the initializer of result3:

  1. The iterator type of ... | take(4) is std::counted_iterator<R>.
  2. The iterator_traits for std::counted_iterator<R> matches the partial specialization std::iterator_traits<std::counted_iterator<I>>.
  3. Said partial specialization derives from std::iterator_traits<I>.
  4. std::iterator_traits<R> is generated from the primary template: it provides a member named iterator_category, but not a member named iterator_concept.
  5. The iterator_category of R is the same as that of T.
  6. The iterator_category of T is input_iterator_tag, because its dereference operator does not return a reference, which is not allowed by C++17 ForwardIterator requirements. (The iterator_concept of T is bidirectional_iterator_tag.)

So in the end, std::iterator_traits<std::counted_iterator<R>> does not provide iterator_concept, and its iterator_category is input_iterator_tag.

Therefore, the result of ... | take(4) fails to model bidirectional_range, and thus it is rejected by views::reverse.

(The iterator type of ... | take(4) is not a counted_iterator when filter is removed from the pipeline; the iterator_category is not input_iterator_tag when transform is removed from the pipeline. So result1 and result2 do not trigger this error.)

This is essentially LWG 3408.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)