From the cppreference.com, I found a simple example about using the std::move:
std::string str = "Hello";
std::vector<std::string> v;
// uses the push_back(const T&) overload, which means
// we'll incur the cost of copying str
v.push_back(str); // First push_back
std::cout << "After copy, str is \"" << str << "\"\n";
// uses the rvalue reference push_back(T&&) overload,
// which means no strings will be copied; instead, the contents
// of str will be moved into the vector. This is less
// expensive, but also means str might now be empty.
v.push_back(std::move(str)); // Second push_back
The comment says that string copy was avoided.
The first push_back will call: void push_back(const value_type& _Val)
The second push_back will call: void push_back(value_type&& _Val)
I checked the implementation code of the two functions:
void push_back(const value_type& _Val)
{ // insert element at end
if (_Inside(_STD addressof(_Val)))
{ // push back an element
size_type _Idx = _STD addressof(_Val) - _Unfancy(this->_Myfirst());
if (this->_Mylast() == this->_Myend())
_Reserve(1);
_Orphan_range(this->_Mylast(), this->_Mylast());
this->_Getal().construct(_Unfancy(this->_Mylast()),
this->_Myfirst()[_Idx]);
++this->_Mylast();
}
else
{ // push back a non-element
if (this->_Mylast() == this->_Myend())
_Reserve(1);
_Orphan_range(this->_Mylast(), this->_Mylast());
this->_Getal().construct(_Unfancy(this->_Mylast()),
_Val);
++this->_Mylast();
}
}
and
void push_back(value_type&& _Val)
{ // insert by moving into element at end
if (_Inside(_STD addressof(_Val)))
{ // push back an element
size_type _Idx = _STD addressof(_Val) - _Unfancy(this->_Myfirst());
if (this->_Mylast() == this->_Myend())
_Reserve(1);
_Orphan_range(this->_Mylast(), this->_Mylast());
this->_Getal().construct(_Unfancy(this->_Mylast()),
_STD forward<value_type>(this->_Myfirst()[_Idx]));
++this->_Mylast();
}
else
{ // push back a non-element
if (this->_Mylast() == this->_Myend())
_Reserve(1);
_Orphan_range(this->_Mylast(), this->_Mylast());
this->_Getal().construct(_Unfancy(this->_Mylast()),
_STD forward<value_type>(_Val));
++this->_Mylast();
}
}
So, according to my understanding, both first push_back (v.push_back(str);
) and second push_back (v.push_back(std::move(str));
) will trigger the vector to construct a std::string
type variable and attach it to the vector.
So, actually in both push_back calls, string was not copied. And, for both push_back, the expense are the same, because both calls basically do the same thing except the second push_back will make the str
input to be empty.
As for the efficiency, the only difference I can think of is that the second push_back will not call the delete []cstring; in the destructor of std::string which makes the second push_back call a little bit more efficient.
Not sure if my understanding is correct. Thanks a lot!
The difference is here:
this->_Getal().construct(_Unfancy(this->_Mylast()),
_STD forward<value_type>(_Val));
vs
this->_Getal().construct(_Unfancy(this->_Mylast()),
_Val);
Now that forward<value_type>
in effect calls std::move
on the std::string
.
In one case, we construct a std::string
by std::string const&
, in the other by a std::string &&
.
So to see the difference, we have to examine what those two different constructors do.
std::string
is usually implemented with the SBO (small buffer optimization). If the string is short (a dozen or so characters), the string is stored within the std::string
.
If it is longer, instead the std::string
stores a pointer to it.
If you have SBO active, both move and copy copy the bytes over. Move may then clear the source.
std::string(std::string const&)
in the case of non-SBO does an allocation and duplicates the buffer containing the characters.
std::string(std::string &&)
in the case of non-SBO moves the pointer over to the destination object, and empties the source object. No memory allocation occurs, and zero bytes are copied.
This is what the push_back(&&)
overload provides.