In C++20, when I use std::ranges::views::take()
on a std::ranges::istream_view()
, the next token in the istream_view
after iterating the take view is skipped.
Consider the following C++20 code snippet:
#include <iostream>
#include <ranges>
#include <sstream>
#include <vector>
#include <algorithm>
namespace rn = std::ranges;
int main() {
std::string input = "1 2 3 4 5 6 7 8 9 10";
std::istringstream input_stream(input);
std::vector<int> head;
rn::copy(rn::istream_view<int>(input_stream) | rn::views::take(5),
std::back_inserter(head));
int next_int = 0;
input_stream >> next_int;
for(auto x : head) std::cout << x << " ";
std::cout << next_int << std::endl;
}
When I compile and run this code in g++14 or clang++17, the following output is produced (notice the missing "6"):
1 2 3 4 5 7
Godbolt link: https://godbolt.org/z/vdGvx9666
Why does this happen? Is this the intended behaviour? What can be done to work around this?
This looks like shortcoming of std::ranges::copy
.
Note that return value of it is:
Return value
A ranges::in_out_result containing an input iterator equal to last and an output iterator past the last element copied.
Now in your case input iterator type is std::ranges::istream_view
and this thing to point after last element copied it have to read (consume) next element. This is necessary to detect if current iterator reached end (of stream).
So to get return value of std::ranges::copy
iterator std::ranges::istream_view
must be progress beyond last element and read next one leading to problem you are osberving.
Note that old fashion std::copy_n
is defined in different way to prevent this problem: note "possible implementation" increases input iterator ins strange way and the fact that input iterator is not returned.