#include <fstream>
#include <string>
#include <vector>
int main() {
auto fin = std::ifstream("tmp.txt");
auto pos = std::istream_iterator<std::string>(fin);
auto last = std::istream_iterator<std::string>{};
auto strs = std::vector<std::string>{};
for (; pos != last; ++pos) {
// Always COPY rather than MOVE the string here!
strs.emplace_back(std::move(*pos));
}
}
Note that strs.emplace_back(std::move(*pos))
will always COPY rather than MOVE the string, even if std::move(*pos)
.
This is due to std::istream_iterator<std::string>::operator*
is defined as follows:
const T& operator*() const;
Which means we cannot move the cached objects even if the iterator is single-pass!
If the C++ standard had defined it as follows
T& operator*() const;
Then, we could
- use
std::istream_iterator<std::string>
if we are sure to move each string exactly once, or,- use
std::istream_iterator<std::string const>
if we would reference the same iterator more than once.
Why did the C++ standard not do so since C++11 has introduced move-semantics? What's the rationale behind the decision?
As another answer says, istream_iterator
existed before C++11 introduced move semantics.
However, for C++20 ranges::istream_view
, its iterator's operator*
always returns Val&
, so you can move the extracted element from inside the istream_view
to outside:
auto fin = std::ifstream("tmp.txt");
auto strs = std::views::istream<std::string>(fin)
| std::views::as_rvalue
| std::ranges::to<std::vector>();