c++c++20std-ranges

What is the category of iota_view iterator?


I was experimenting with std::iterator_traits and std::views::iota. And suddenly I found out that std::iterator_traits returns unexpected type for an iterator category.

libc++:

#include <ranges>

int main() {
    auto view = std::views::iota(0ULL, 10000000000ULL);

    static_assert(
      std::is_same_v<
        std::iterator_traits<decltype(view.begin())>::iterator_category
      , std::input_iterator_tag>
    );
}

libstdc++:

#include <ranges>

int main() {
    auto view = std::views::iota(0ULL, 10000000000ULL);

    static_assert(
      std::is_same_v<
        std::iterator_traits<decltype(view.begin())>::iterator_category
      , std::output_iterator_tag>
    );
}

What is the reason for that? Why does category from traits differ from std::random_access_tag?


Solution

  • The reference type of iota_view<long long unsigned, long long unsigned>::iterator is prvalue, so it can only be Cpp17InputIterator at most.

    In libstdc++, its difference type is __int128, which is an integer-class type introduced to resolve integer overflow. Since Cpp17InputIterator requires that the difference type of the iterator must only be of integer type, it fails to satisfy Cpp17InputIterator, which makes it only Cpp17Iterator. So iterator_traits::iterator_category will be output_iterator_tag because C++17 output iterators do not require the difference type to exist.

    libc++ doesn't implement any integer-class type, so its difference type is just simple long long, which is a Cpp17InputIterator.