问题
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.
回答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-constreference
typedef; - write your own
move_iterator
performingconst_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.
回答2:
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.
来源:https://stackoverflow.com/questions/31985373/can-one-make-move-iterator-from-istream-iterator