0

Consider the following example:

int main()
{
    std::string_view foo = "This is a test";
    
    auto split_foo = foo |
                        std::views::split(' ') |
                        std::ranges::views::transform( 
                            []( const auto &word )
                            {
                                return std::string_view{std::begin(word),std::end(word)};
                            } );

    auto it = std::begin(split_foo);
    while (it != std::end(split_foo))
    {
        std::cout<< "-> " << *it <<std::endl;
        it = std::next(it);
    }
}

That produces the following output:

-> This
-> is
-> a
-> test

I would like to iterate until one-before-end, such that the output is:

-> This
-> is
-> a

According to the following reference from std::ranges::prev

... for some underlying ranges, ranges::transform_view::end doesn't have the same return type as ranges::transform_view::begin, and so --r.end() won't compile. This isn't something that ranges::prev can aid with, but there are workarounds.

Does anyone know a workaround to decrement the end iterator from std::ranges::views::transform?

ps. I have tried std::next(std::end(split_foo), -1), which compiles but crashes the program because the iterator is not bidirectional.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
Constantinos Glynos
  • 2,952
  • 2
  • 14
  • 32

1 Answers1

1

Just change the while-loop to:

auto it = split_foo.begin();
auto next = std::ranges::next(it);
while (next != split_foo.end()) {
  std::cout<< "-> " << *it <<std::endl;
  it = next;
  next = std::ranges::next(next);
}

Demo

Do not use std::xxx to operate iterators when dealing with C++20 <ranges>, instead, you should use ranges::xxx, since the former is not compatible with the C++20 iterator system.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
  • 2
    This feels like it does extra work calling `next` twice per loop. You should probably factor this out into an `if` and a `do{}while()` to avoid it. – Mike Vine Jan 05 '23 at 16:51
  • nice and neat, thanks! I am interested to know more about `the former is not compatible with the C++20 iterator system`. Where did you get this from? – Constantinos Glynos Jan 05 '23 at 16:52
  • 1
    @ConstantinosGlynos For example, [some C++20 random access iterators are just input iterators in C++17](https://stackoverflow.com/questions/67606563/what-is-the-difference-between-iterator-category-and-iterator-concept-in-c20), so you can't apply `std::prev` to it but you can decrement it with `ranges::prev`. – 康桓瑋 Jan 05 '23 at 17:12
  • @康桓瑋: Lovely! +1 for the question and +1 to the answer. – Constantinos Glynos Jan 05 '23 at 17:17