4

Consider following code:

typedef istream_iterator<char> char_itr ;
char_itr eos;

string ll("some text here");

istringstream line_in(ll);
char_itr start(line_in);

move_iterator<char_itr> mstart(start); // !!!
move_iterator<char_itr> meos(eos);
vector<char> vc(mstart, meos);

Above code will not compile because of line (!!!):

error C2440: 'return' : cannot convert from 'const char' to 'char &&'

But if you replace mstart and meos with start and eos, respectively (regular iterators), the code will compile. Why I can't make move_iterators?

EDIT: For those wondering why I would like to move a character from a stream/string. Actual problem involves more complex data type than char which copying from a string should be avoided. char was used just for the sake of simplicity, to present the mechanism causing error.

Piotr G
  • 959
  • 1
  • 7
  • 25
  • Think about what you're trying to do for a while, do you really think you can *move* characters *from* a stream? It seems to me that you're trying to do some premature optimization. – Some programmer dude Aug 13 '15 at 10:23
  • There is no difference between moving and copying a `char` anyway. It's just a byte! – Tobias Brandt Aug 13 '15 at 10:25
  • http://cplusplus.github.io/LWG/lwg-defects.html#2106 – T.C. Aug 13 '15 at 10:27
  • @ecatmur The issue title is misleading. The definition of `move_iterator::reference` is changed even for this case. – T.C. Aug 13 '15 at 10:35
  • @ecatmur You are still missing the point. Pre-LWG2106, `move_iterator::reference` is `I::value_type &&` aka `T&&`. Post-LWG2106, `move_iterator::reference`, for `I::reference` being a real reference, is `std::remove_reference_t&&` aka `T const&&`. (`iterator_traits` dance omitted for simplicity.) – T.C. Aug 13 '15 at 10:44
  • @T.C. ah, I understand now. So, post LWG2106 it'll compile but it'll do the wrong thing in this case, since the `move_iterator` will return `T const&&`. Possibly not an improvement. Thanks! – ecatmur Aug 13 '15 at 10:57

2 Answers2

1

There was a discussion of this on the std-discussion newsgroup earlier this year: https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/h7jGY95j1oc

The consensus appears to be that istream_iterator::reference is T const& to enforce the InputIterator contract; that is, to prevent users writing *it = value;. Unfortunately, this also prevents moving from the cached value.

As T.C. mentioned above, post the resolution to LWG2106 the code will compile; unfortunately because move_iterator::reference will be T const&& it will silently do the wrong thing, most likely invoking the copy constructor of your type.

Since istream_iterator modifies the cached value when incremented, it is (from a language POV) legal to const_cast the returned reference to T&. Unfortunately (again) that doesn't help here as there's no easy way to interpose a const_cast between the istream_iterator and move_iterator.

Possible workaround solutions:

  • write your own istream_iterator, with a non-const reference typedef;
  • write your own move_iterator performing const_cast;
  • write an interposing const_cast iterator;
  • use a mutable wrapper around the value type.

The latter option is surprisingly easy:

template<class T>
  struct mutable_wrapper {
    T mutable value;
    operator T&() const { return value; }
  };
// ...
using itr = std::istream_iterator<mutable_wrapper<MyType>>;

Example.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
0

Looking at istream_iterator::reference, we see that it's T const &. Dereferencing the iterator gives us such a reference. But to be able to move from something, that something needs to be modifiable. That's what the error message is trying to tell you.

You could make a move_iterator from an istream_iterator, by putting some custom iterator that keeps an internal (non const) storage inbetween.

But why would you want to move from an istream_iterator? Those are single pass iterators, and thus likely have nearly no internal storage.

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63