Search code examples
c++iteratorc++17stdvectormove-semantics

It looks like the std::move_iterator only works with string data types


It is possible to move data from source container to destination container using the std::move_iterator if the container has string types of data. std::move_iterator does not operate with other data types in the container.

std::move_iterator works fine

#include <iostream>     // std::cout
#include <iterator>     // std::move_iterator
#include <vector>       // std::vector
#include <string>       // std::string
#include <algorithm>    // std::copy

using namespace std;
int main () {
    
    vector<string>src = {"one", "two", "three", "four"}, des(4);
    using Iter = vector<string>::iterator;
    
    //move the data from src to des
    move_iterator<Iter>beginitr(src.begin());
    move_iterator<Iter>enditr(src.end());
    Iter diter = des.begin();
    
    while(beginitr != enditr)
    {
        *diter++ = *beginitr++;
    }
    
    cout << "src container size: " << src.size() << endl;
    cout << "data: " ;
    for_each(begin(src),end(src), [](string x){cout << x ;});
    cout << endl << "THE END";

  return 0;
}

output

src container size: 4
data: 
THE END

std::move_iterator dose not move the data

#include <iostream>     // std::cout
#include <iterator>     // std::move_iterator
#include <list>       // std::vector
#include <string>       // std::string
#include <algorithm>    // std::copy

using namespace std;
int main () {
    
    list<int>src = {1,2,3,4}, des(4);
    using Iter = list<int>::iterator;
    
    //move the data from src to des
    move_iterator<Iter>beginitr = make_move_iterator(src.begin());
    move_iterator<Iter>enditr = make_move_iterator(src.end());
    Iter diter = des.begin();
    
    while(beginitr != enditr)
    {
        *diter++ = *beginitr++;
    }
    
    cout << "src container size: " << src.size() << endl;
    for_each(begin(src),end(src), [](int x){cout << x << endl;});
    

  return 0;
}

output

src container size: 4
1
2
3
4

It appears that integer data gets copied to the target container rather than being moved.


Solution

  • The only difference using a move iterator makes is that dereferencing the iterator yields an rvalue reference. If only the copy assignment operator is available, but no move assignment operator, this will be used as "fallback".

    The following code demonstrates this:

    #include <algorithm>
    #include <iostream>
    #include <iterator>
    #include <list>
    #include <utility>
    
    struct Test
    {
    #if 0
        Test(Test&&)
        {
            std::cout << "Test::Test(Test&&)\n";
        }
    
        Test& operator=(Test&&)
        {
            std::cout << "Test& Test::operator=(Test&&)\n";
            return *this;
        }
    #endif 
        
        Test(Test const&)
        {
            std::cout << "Test::Test(Test const&)\n";
        }
    
        Test& operator=(Test const&)
        {
            std::cout << "Test& Test::operator=(Test const&)\n";
            return *this;
        }
    
        Test()
        {
            std::cout << "Test::Test()\n";
        }
    
    };
    
     
    int main() {
        std::list<Test> src(4);
        std::list<Test> des(4);
    
        std::cout << "-------------------copy with move iterator--------------------\n";
        std::copy(std::make_move_iterator(src.begin()), std::make_move_iterator(src.end()), des.begin());
    
        std::cout << "-------------------copy with normal iterator--------------------\n";
        std::copy(src.begin(), src.end(), des.begin());
    }
    

    Output with #if 0 left as is:

    ...
    -------------------copy with move iterator--------------------
    Test& Test::operator=(Test const&)
    Test& Test::operator=(Test const&)
    Test& Test::operator=(Test const&)
    Test& Test::operator=(Test const&)
    -------------------copy with normal iterator--------------------
    Test& Test::operator=(Test const&)
    Test& Test::operator=(Test const&)
    Test& Test::operator=(Test const&)
    Test& Test::operator=(Test const&)
    

    Output with move semantics defined (#if 1 instead of #if 0):

    ...
    -------------------copy with move iterator--------------------
    Test& Test::operator=(Test&&)
    Test& Test::operator=(Test&&)
    Test& Test::operator=(Test&&)
    Test& Test::operator=(Test&&)
    -------------------copy with normal iterator--------------------
    Test& Test::operator=(Test const&)
    Test& Test::operator=(Test const&)
    Test& Test::operator=(Test const&)
    Test& Test::operator=(Test const&)
    

    Godbolt Demo

    For arithmetic types like int there are only copy semantics, not move semantics leaving the source list unmodified in your code.