Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
310 views
in Technique[技术] by (71.8m points)

c++ - How does std::copy work with stream iterators

A usual STL construct is:

vector<string> col;
copy(istream_iterator<string>(cin), istream_iterator<string>(),
    back_inserter(col));

where we use an istream_iterator to copy from std input (cin) to a vector.

Can anyone explain how this code works?

my problem is that I don't really understand this part:

istream_iterator<string>(cin), istream_iterator<string>()
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

First, note that in this case, there's no real need to use std::copy at all. You can just initialize the vector directly from the iterators:

vector<string> col((istream_iterator<string>(cin)),
                    istream_iterator<string>());

This probably doesn't make the code a whole lot easier to understand though.

As far as how the code works, it's probably a little more straighforward than you think. An istream_iterator looks vaguely like this:

template <class T>
class istream_iterator { 
    std::istream *is;
    T data;
public:
    istream_iterator(std::istream &is) : is(&is) { ++(*this); }
    istream_iterator() : is(nullptr) {}

    T operator++() { (*is) >> data; return *this; }
    T operator++(int) { (*is) >> data; return *this; }

    T const &operator*() { return data; }   

    bool operator !=(istream_iterator &end) { return (*is).good(); }
    bool operator ==(istream_iterator &end) { return !(*is).good(); }
};

Obviously there's more more I'm skipping over, but that's most of what we care about here. So, what happens is that when you create the iterator, it reads (or attempts to) an item from the stream into the variable I've called data. When you dereference the iterator, it returns data. When you increment the iterator, it reads (or attempts to) the next item from the file. Despite being written as if they compare one iterator to another, operator== and operator!= really just check for the end of the file1.

That's then used by std::copy, which (again simplified) looks vaguely like this:

template <class InIt, class OutIt>
void std::copy(InIt b, InIt e, OutIt d) { 
    while (b != e) {
        *d = *b;
        ++b;
        ++d;
    }
}

So, this reads and item from the input iterator, writes that item to the output iterator, and repeats until the iterator for the current position compares equal to the iterator for the end of the input (which will happen when you reach the end of the file). Note that unlike other iterators, the only "end" position you're allowed to use with an istream iterator is the end of the file.


  1. Note that technically, this isn't conforming behavior. I've simplified comparison to keep things simple. Two default-constructed iterators should compare equal, and if you construct two iterators from the same stream, they should compare equal at least before you've read anything from the stream. This makes little practical difference though -- the only comparison you seen in real use is to determine whether you've reached the end of file yet.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...