c++c++11istream

Why does `std::istream::seekg()` affect the behavior of `std::istreambuf_iterator`?


Why does std::istream::seekg() affect the behavior of std::istreambuf_iterator?

I carefully read the introduction about std::istreambuf_iterator on cppreference, but there is no direct answer for this matter.

Here is the code snippet:

#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
 
int main()
{
    //with seekg to the end
    {
        std::cout << "#1 with seekg" << std::endl;
        std::istringstream in{"Hello, world"};
        in.seekg(std::istream::end);
        std::istreambuf_iterator<char> it{in}, end;
        std::string ss{it, end};
        std::cout << ss << std::endl;
    }

    //without seekg
    {
        std::cout << "#2 without seekg" << std::endl;
        std::istringstream in{"Hello, world"};
        std::istreambuf_iterator<char> it{in}, end;
        std::string ss{it, end};
        std::cout << ss << std::endl;
    }
    
    //seekg to end and then to begin
    {
        std::cout << "#3 seekg to end and then to begin" << std::endl;
        std::istringstream in{"Hello, world"};
        in.seekg(std::istream::end);
        std::streampos pos = in.tellg();
        std::cout << "len:" << static_cast<long long>(pos) << std::endl;
        in.seekg(std::istream::beg);
        std::istreambuf_iterator<char> it{in}, end;
        std::string ss{it, end};
        std::cout << ss << std::endl;
    }
}

Here is the output:

#1 with seekg
llo, world
#2 without seekg
Hello, world
#3 seekg to end and then to begin
len:2
Hello, world

I think the the ouput should be nothing or Hello, world for the fist case while the ouput is llo,world.


Solution

  • std::basic_istream::seekg() moves input position indicator to the given position. According to the documentation the position is set by a numeric value.

    The std::istreambuf_iterator, when it is created for the given istream, will point to the current input position. So since seekg() modifies the input position value for the given istream, its call before creating iterator can have some affect on the iterator.

    Now let's answer the question in the end of your post: why don't you observe empty string or full string in your code?

    According to documentation seekg() has two overloads:

    1. it can take as an argument single numeric value, which represents an desired position relative to the beginning of the stream.
    2. it can take as an argument numeric offset and some flag value to indicate the direction of that offset. This direction can be specified by std::ios_base::beg, std::ios_base::end, std::ios_base::curr values.

    Now, in your code you pass only one argument to seekg() method. Thus you invoke the first overload. But you pass to it some std::istream::end, which is, I assume, std::ios_base::end value, somehow imported into std::istream namespace. And you are lucky enough, so that its value is (or can be implicitly converted to) integer with value 2.

    So basically in your code you write in.seekg(2), which results in moving the input position of the stream to the third character in your string (the character with index 2). As a result llo, world is printed.

    In order to move the input position to the end of the buffer use in.seekg(0, std::ios_base::end); In order to move the position to the beginning of the buffer use in.seekg(0, std::ios_base::beg); or in.seekg(0);

    By the way, your third example, where you moved input position "to the end and back" worked because std::istream::beg value is 0. So effectively there you did call in.seekg(0) before creating iterator and it read the string from its start.