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;
}
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.
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.
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.
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.
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.
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.
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.
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.
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.
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_iterator
s. 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With