No C++ 20, quando uso std::ranges::views::take()
em a std::ranges::istream_view()
, o próximo token após a istream_view
iteração da visualização take é ignorado.
Considere o seguinte trecho de código C++20:
#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;
}
Quando compilo e executo este código em g++14 ou clang++17, a seguinte saída é produzida (observe o "6" ausente):
1 2 3 4 5 7
Link Godbolt: https://godbolt.org/z/vdGvx9666
Por que isso acontece? Este é o comportamento pretendido? O que pode ser feito para contornar isso?
Isso parece uma deficiência de
std::ranges::copy
.Observe que o valor de retorno disso é:
std::ranges::copy, std::ranges::copy_if, std::ranges::copy_result, std::ranges::copy_if_result - cppreference.com
Agora, no seu caso, o tipo de iterador de entrada é
std::ranges::istream_view
e essa coisa para apontar após o último elemento copiado, ele deve ler (consumir) o próximo elemento. Isso é necessário para detectar se o iterador atual atingiu o fim (do fluxo).Portanto, para obter o valor de retorno do
std::ranges::copy
iterador,std::ranges::istream_view
deve-se progredir além do último elemento e ler o próximo que leva ao problema que você está enfrentando.Observe que a moda antiga
std::copy_n
é definida de maneira diferente para evitar esse problema: observe que a "possível implementação" aumenta o iterador de entrada de uma maneira estranha e o fato de que o iterador de entrada não é retornado.