Search code examples
c++c++11iteratormove-semantics

What is move_iterator for


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.


Solution

  • 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.