Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are std::move and std::copy identical?

I tried to do something like:

std::copy(std::make_move_iterator(s1.begin()), std::make_move_iterator(s1.end()), 
          std::make_move_iterator(s2.begin()));

And got this error:

error: using xvalue (rvalue reference) as lvalue
        *__result = std::move(*__first);

Which seemed confusing to me. The same thing happens if you use std::move. It appears that GCC internally uses a function called std::__copy_move_a which moves rather than copies. Does it matter whether you use std::copy or std::move?


#include <string>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cstring>

struct Test
{
    typedef std::string::value_type value_type;
    std::string data;

    Test()
    {
    }

    Test(const char* data)
        : data(data)
    {
    }

    ~Test()
    {
    }

    Test(const Test& other)
        : data(other.data)
    {
        std::cout << "Copy constructor.\n";
    }

    Test& operator=(const Test& other)
    {
        data = other.data;
        std::cout << "Copy assignment operator.\n";
        return *this;
    }

    Test(Test&& other)
        : data(std::move(other.data))
    {
        std::cout << "Move constructor.\n";
    }

    decltype(data.begin()) begin()
    {
        return data.begin();
    }

    decltype(data.end()) end()
    {
        return data.end();
    }

    void push_back( std::string::value_type ch )
    {
        data.push_back(ch);
    }
};

int main()
{
    Test s1("test");
    Test s2("four");
    std::copy(std::make_move_iterator(s1.begin()), std::make_move_iterator(s1.end()), 
              std::make_move_iterator(s2.begin()));
    std::cout << s2.data;
}
like image 523
user4155097 Avatar asked Oct 17 '14 20:10

user4155097


People also ask

Does std :: move make a copy?

std::move is actually just a request to move and if the type of the object has not a move constructor/assign-operator defined or generated the move operation will fall back to a copy.

Is std :: move faster than copy?

std::string: The std::string on Linux behaves strangely. On one hand, copying is very fast; on the other hand, moving is only 16 times faster than copying. The becomes even more strange if I compile and execute the program without optimization.

Is move better than COPY C++?

It's faster because moving allows the source to be left in a invalid state, so you can steal it's resources. For example, if a object holds a pointer to a large block of allocated memory, a move can simply steal the pointer while a copy must allocate its own memory and copy the whole memory block.

Why is move more efficient than copy?

Move constructor moves the resources in the heap, i.e., unlike copy constructors which copy the data of the existing object and assigning it to the new object move constructor just makes the pointer of the declared object to point to the data of temporary object and nulls out the pointer of the temporary objects.

When should you not use std move?

However, std::move must be used judiciously; using it blithely may lead to performance degradation, or simply be redundant, affecting readability of the code. Fortunately, the compiler can sometimes help with finding such wrong uses of std::move.

What is the difference between move () and copy () in C++?

The copy constructor uses the lvalue references which are marked with one ampersand (&) while the move constructor uses the rvalue references are marked with two ampersands (&&). std::move () is a function used to convert an lvalue reference into the rvalue reference.

Is move semantic faster than copy semantic?

On one hand, copying is very fast; on the other hand, moving is only 16 times faster than copying. The becomes even more strange if I compile and execute the program without optimization. I get the result on Linux that move semantic is only 1.5 times faster than the copy semantic.

Why can’t I call move constructor instead of copy constructor?

The problem here is that parameters a and b are l-value references, not r-value references, so we don’t have a way to invoke the move constructor and move assignment operator instead of copy constructor and copy assignment. By default, we get the copy constructor and copy assignment behaviors.


2 Answers

std::move(a, b, c); is semantically identical to

std::copy(std::make_move_iterator(a),
          std::make_move_iterator(b),
          c);

Your efforts to use them both failed because the third argument - the output iterator - should not be a move iterator. You are storing into the third iterator, not moving from it. Both

std::copy(std::make_move_iterator(s1.begin()),
          std::make_move_iterator(s1.end()),
          s2.begin());

and

std::move(s1.begin(), s1.end(), s2.begin());

should do what you want.

like image 129
Casey Avatar answered Sep 22 '22 07:09

Casey


std::move moves the elements if possible, and copies otherwise. std::copy will always copy.

libstdc++'s copy_move_a also takes a template parameter _IsMove. That, and the iterator types, it delegates to a __copy_move class template that is partially specialized for different iterator categories, etc. but most importantly: Whether to move or not. One of the specializations is

#if __cplusplus >= 201103L
  template<typename _Category>
    struct __copy_move<true, false, _Category>
    // first specialized template argument is whether to move
    {
      template<typename _II, typename _OI>
        static _OI
        __copy_m(_II __first, _II __last, _OI __result)
        {
      for (; __first != __last; ++__result, ++__first)
        *__result = std::move(*__first); // That may be your line
      return __result;
    }
    };
#endif

Your code fails to compile for a completely different reason: The second range is given through move_iterators. If you dereference them, they return an rvalue reference to the object - and you can't assign something to an xvalue of scalar type.

int i;
std::move(i) = 7; // "expression not assignable" -- basically what your code does

The std::move is implicitly included in *__result and is of the same value category, that is, an xvalue.

For your example,

std::copy(std::make_move_iterator(s1.begin()), std::make_move_iterator(s1.end()),
          s2.begin());

should work fine.

like image 39
Columbo Avatar answered Sep 22 '22 07:09

Columbo