What is move_iterator for

Min Lin picture Min Lin · Jan 20, 2015 · Viewed 7.2k times · Source

If I understand it correct, a=std::move(b) binds reference a to the address of b. And after this operation the content that b points to is not guaranteed.

The implementation of move_iterator here has this line

auto operator[](difference_type n) const -> decltype(std::move(current[n]))
  { return std::move(current[n]); }

However, I don't think it makes sense to std::move an element in an array. What happens if a=std::move(b[n])?

The following example confuses me also:

std::string concat = std::accumulate(
                             std::move_iterator<iter_t>(source.begin()),
                             std::move_iterator<iter_t>(source.end()),
                             std::string("1234"));

Since the concat will itself allocate a continuous chunk of memory to store the result, which will not have any overlap with source. The data in source will be copied to concat but not moved.

Answer

Tony Delroy picture Tony Delroy · Jan 20, 2015

If I understand it correct, a=std::move(b) binds reference a to the address of b. And after this operation the content that b points to is not guaranteed.

Ah, no: a is not necessarily a reference. The above use of std::move also grants the compiler permission to call decltype(a)::operator=(decltype(b)&&) if it exists: such assignment operators are used when during the assignment to a the value of b need not be preserved, but b must still be left in some sane state for destruction.

However, I don't think it makes sense to std::move an element in an array. What happens if a=std::move(b[n])?

It can make sense... it just means that each array elements may be efficiently assigned/moved to another variable, but only once per element. After they've been moved-from, a properly-written move constructor or assignment operator should leave objects in a valid but unspecified state, which means you'd usually want to set them again before reading from them.

My answer here shows how someone could append/move elements from a list to a vector. With current C++ Standards, you can create move_iterators directly like that.

The code below shows how - even with older compilers / C++ Standards - make_move_iterator can be used with std::copy if you want to move from the elements in the source iterator range.

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

struct X
{
    X(int n) : n_(n) { }
    X(const X& rhs) : n_(rhs.n_) { }
    X(X&& rhs) : n_{ rhs.n_ } { rhs.n_ *= -1; std::cout << "=(X&&) "; }
    X& operator=(X&& rhs) { n_ = rhs.n_; rhs.n_ *= -1; std::cout << "=(X&&) "; return *this; }
    int n_;
};

int main()
{
    std::vector<X> v{2, 1, 8, 3, 4, 5, 6};
    std::vector<X> v2{};

    std::copy(v.begin() + 2, v.end(), std::insert_iterator(v2, v2.end()));
    for (auto& x : v)
        std::cout << x.n_ << ' ';
    std::cout << '\n';

    std::copy(std::make_move_iterator(v.begin() + 2), std::make_move_iterator(v.end()), std::insert_iterator(v2, v2.end()));
    for (auto& x : v)
        std::cout << x.n_ << ' ';
    std::cout << '\n';
}

Output:

2 1 8 3 4 5 6 
=(X&&) =(X&&) =(X&&) =(X&&) =(X&&) 2 1 -8 -3 -4 -5 -6 

Code can be run / edited on coliru.