c++istreamstd-rangesc++23istream-iterator

Why can't I use lazy_split_view with a istream view instead of a string_view?


Consider this code snippet (adapted from cppreference.com):

constexpr std::string_view text{"Hello-+-C++-+-23-+-!"};
constexpr std::string_view delim{"-+-"};
std::ranges::for_each(text | std::views::lazy_split(delim), act_on_substring);

I want to do the same for a stream - say, std::cin - instead of text. I've tried:

auto isv = std::views::istream<std::string>(std::cin);
constexpr std::string_view delim{"-+-"};
std::ranges::for_each(isv | std::views::lazy_split(delim), act_on_substring);

But this code fails to compile:

<source>:21:31: error: invalid operands to binary expression ('basic_istream_view<basic_string<char, char_traits<char>, allocator<char>>, char, char_traits<char>>' and '_Partial<_LazySplit, decay_t<const basic_string_view<char, char_traits<char>> &>>' (aka '_Partial<std::ranges::views::_LazySplit, std::basic_string_view<char, std::char_traits<char>>>'))
   21 |     std::ranges::for_each(isv | std::views::lazy_split(delim), act_on_substring);
      |                           ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

why?


Solution

  • Although views::lazy_split supports input_range, since the latter is a single-pass range, there is no way to go back to the previous element once iterated. In this case, the pattern range must be a tiny-range, which means its size must be smaller than or equal to 1.

    The most you can do is:

    std::ranges::for_each(
      std::views::istream<char>(std::cin) | std::views::lazy_split('+'),
      act_on_substring
    );
    

    Demo