3

I want a view of the characters from an input stream:

    auto input = std::stringstream{"abcd"};
    using Iter = std::istreambuf_iterator<char>;
    auto s = std::ranges::subrange{Iter{input}, Iter{}};

So far, so good. Now, I transform that view (using an identity transform for simplicity):

    auto t = s | std::views::transform(std::identity{});

Although this transformed view has a valid value_type (i.e. this assertion passes):

    using T = decltype(t);
    static_assert(std::is_same_v<char, std::ranges::range_value_t<T>>);

the iterator type from it doesn't have one:

    static_assert(std::is_same_v<char, std::iterator_traits<std::ranges::iterator_t<T>>::value_type>);

This fails with

view.cc: In function 'int main()':
view.cc:27:90: error: 'value_type' is not a member of 'std::iterator_traits<std::ranges::transform_view<std::ranges::subrange<std::istreambuf_iterator<char, std::char_traits<char> >, std::istreambuf_iterator<char, std::char_traits<char> >, std::ranges::subrange_kind::unsized>, std::identity>::_Iterator<false> >'
   27 |     static_assert(std::is_same_v<char, std::iterator_traits<std::ranges::iterator_t<T>>::value_type>);
      |                                                                                          ^~~~~~~~~~
view.cc:27:100: error: template argument 2 is invalid
   27 |     static_assert(std::is_same_v<char, std::iterator_traits<std::ranges::iterator_t<T>>::value_type>);
      |                   

The reason that it is important to me is that I want to use the iterator traits in a downstream view adapter.


Investigation suggests that the transform view's iterator is missing its iterator_category member. The code succeeds when I pass a more capable range such as std::string to the transform, but fails with this non-forward range.


Full code (also on Compiler Explorer):

#include <functional>
#include <iterator>
#include <ranges>
#include <sstream>
#include <string>
#include <type_traits>

int main()
{
#ifdef PASS
    auto input = std::string{"abcd"};
    std::ranges::forward_range auto s = std::ranges::subrange(input.begin(), input.end());
#else
    auto input = std::stringstream{"abcd"};
    using Iter = std::istreambuf_iterator<char>;
    std::ranges::input_range auto s = std::ranges::subrange{Iter{input}, Iter{}};
#endif

    using S = decltype(s);
    static_assert(std::is_same_v<char, std::ranges::range_value_t<S>>);
    static_assert(std::is_same_v<char, std::iterator_traits<std::ranges::iterator_t<S>>::value_type>);

    auto t = s | std::views::transform(std::identity{});
    using T = decltype(t);
    static_assert(std::is_same_v<char, std::ranges::range_value_t<T>>);
    static_assert(std::is_same_v<char, std::ranges::iterator_t<T>::value_type>);
    static_assert(std::is_base_of_v<std::input_iterator_tag, std::ranges::iterator_t<T>::iterator_category>);
    static_assert(std::is_same_v<char, std::iterator_traits<std::ranges::iterator_t<T>>::value_type>);
}

1 Answer 1

7

Investigation suggests that the transform view's iterator is missing its iterator_category member.

When transform_view is applied to an underlying range, its iterator's post-increment operator (operator++(int)) returns a copy of it only when the underlying range models forward_range (which is reasonable since pre-incrementing the iterator of input_range invalidates all copies of it).

Since the subrange of istreambuf_iterator is just an input_range, transform_view's iterator's post-increment operator returns nothing (void), which does not meet the requirements of Cpp17InputIterator, which requires that *it++ to be well-formed, i.e., the return type of the post-increment operator should be dereferenceable.

So in your example, transform_view's iterator is not a valid C++17 input iterator, it's only a C++17 output iterator, which makes iterator_traits has no value_type member.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.