Itachi Uchiwa
Itachi Uchiwa

Reputation: 3164

Why I cannot create a stream iterator?

I am supposed to create a looks-like stream iterator class so that I can read from the input stream when incrmenting an object of my class.

I've done this:

template<class T>
struct istrm_it{
    istrm_it() = default;
    istrm_it(std::istream& in) :
        in_(in), val_(T()) {
        in_ >> val_;
    }
    T val_;
    std::istream& in_ = std::cin;
    istrm_it& operator++() { in >> val_; return *this; };
    istrm_it& operator++(int) { in_ >> val_; return *this; };
    T operator*() { return val_; }
    bool operator==(const istrm_it& rhs)const { return in_ == rhs.in_; }
    bool operator!=(const istrm_it& rhs) const{ return in_ != rhs.in_; }
};

int main(){

    istrm_it<int> it(std::cin), e;
    cout << *it << endl; // ok

    it++; // ok read the next value from the nput stream

    vector<int> vi;
    while (it != e) // the problem here
        vi.push_back(*it++);

    for (auto i : vi)
        std::cout << i << ", ";
    std::cout << std::endl;


    std::cout << std::endl;
}

What I get: Severity Code Description Project File Line Suppression State Error C2678 binary '!=': no operator found which takes a left-hand operand of type 'std::istream' (or there is no acceptable conversion)

Upvotes: 3

Views: 102

Answers (2)

Raindrop7
Raindrop7

Reputation: 3911

As @Some programmer dude: Stream objects can neither be copied, assigned nor compared.

If you want to make it works for some educational purpose then you can add another member to your class istrm_iter that keeps track of the stream state it is bound to and in in/equality operators compare there iterators whether their internal state is good or not so if both of them are good then they are equal and this occur when they are off-end otherwise they are not equal:

The default constructor which doesn't bind the iterator to a stream should make the internal state bad.

And the ++ should check after each input operator whether the stream hits the EOF or had reported an IO error thus it sets the status to bad. which makes it equal to the one constructed with default constructor (as off-the-end iterator).

Here is a simple suggestion:

template<class T>
struct istrm_it {
    istrm_it() = default;
    istrm_it(std::istream& in) :
        in_(in), val_(T()),
        is_good_(true) {
        in_ >> val_;
    }
    bool is_good_ = false;
    T val_ = T{};
    std::istream& in_ = std::cin;
    istrm_it& operator++() { in_ >> val_; if (!in_) is_good_ = false; return *this; };
    istrm_it& operator++(int) { in_ >> val_;  if (!in_) is_good_ = false; return *this; };
    T operator*() { return val_; }
    bool operator==(const istrm_it& rhs) const {
        return is_good_ && rhs.is_good_ || !is_good_ && !rhs.is_good_;}
    bool operator!=(const istrm_it& rhs) const{ return !(operator==(rhs)); }
};

and in main:

int main(){
    istrm_it<int> it(std::cin), e;
    cout << *it << endl;

    vector<int> vi;

    while (it != e)
        vi.push_back(*it++);


    for (auto i : vi)
        std::cout << i << ", ";
    std::cout << std::endl;
}

No it works. N.B This is just an example I know in real it takes a lot of stuff to mimick the class defined by the standard library.

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409166

The problem is in the comparison in_ != rhs.in_ (and in_ == rhs.in_ as well). You can't compare streams.

Instead you need to keep some kind of "is end iterator" or "is at end-of-file" state that is set when you reach end-of-file, and which is set by default in the default-constructed iterator object.

Upvotes: 7

Related Questions