Search code examples
c++istreamconst-iteratoristream-iterator

Is there a istream const iterator?


Is there such a thing as a std::istream const iterator?

The following code won't compile because the std::istream_iterator in foo() can't bind to the const std::istream reference to the temporary object created in main().

// main.cpp
#include <iostream>
#include <sstream>
#include <iterator>

void foo( const std::istream& s )
{
  std::istream_iterator<char> i( s );
  // No std::istream_const_iterator, but anything by another name?
  std::cout << *i;
}

int main( int argc, char* argv[] )
{
  std::string p( "abcdefghijklmnopqrstuvwxyz" );
  foo( std::stringstream(p) );

  return 0;
}

Is there a istream iterator that can bind to a const istream?

Having just asked this question, I just learned that istream_iterator ctors initialize and perform the first read. I guess I'm still not clear: does that read necessarily modify the bound istream? If not, it seems like there ought to be some kind of const iterator that can bind to a const istream, no?

$ g++ --version
g++ (GCC) 9.2.1 20190827 (Red Hat 9.2.1-1)
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -g ./main.cpp && ./a.out
./main.cpp: In function ‘void foo(const istream&)’:
./main.cpp:84:34: error: binding reference of type ‘std::istream_iterator<char>::istream_type&’ {aka ‘std::basic_istream<char>&’} to ‘const istream’ {aka ‘const std::basic_istream<char>’} discards qualifiers
   84 |   std::istream_iterator<char> i( s );
      |                                  ^
In file included from /usr/include/c++/9/iterator:66,
                 from ./main.cpp:80:
/usr/include/c++/9/bits/stream_iterator.h:68:38: note:   initializing argument 1 of ‘std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_iterator(std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_type&) [with _Tp = char; _CharT = char; _Traits = std::char_traits<char>; _Dist = long int; std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_type = std::ba
sic_istream<char>]’
   68 |       istream_iterator(istream_type& __s)
      |                        ~~~~~~~~~~~~~~^~~

Solution

  • does that read necessarily modify the bound istream?

    Yes.

    istream_iterator is a convenience class that allows one to treat istream objects as though they are containers such as a std::vector or an array.

    Underneath, the istream is the object used to read from the stream. And yes, reading from a stream does modify an istream. How else would the istream keep track of internal state to indicate whether the attempt to read was successful or not, how many characters were read, etc.?

    Since you need a non-const istream objects to read, it make no sense to be able to construct a istream_iterator from const istream objects.