Reputation: 869
std::string t1("aaa");
const char *p = t1.c_str();
std::string t2(std::move(t1));
if (p == t2.c_str()) {
std::cout << "ok!" << std::endl;
}
This code printed nothing in vs2012. It just use memmove
internally to copy the string in t1
to t2
. Why?
Upvotes: 2
Views: 319
Reputation: 2615
Specifically what's going on here is "small string optimization".
Visual C++'s STL (and many others) have "fat" std::string
classes that can hold a small string internally with zero memory allocations required. This is an optimization for some common use cases.
Even with std::move
, since your string is small enough that it is store entirely inside the std::string
itself and so must be copied. If you tried with a larger string your code would probably work, though there's absolutely no guarantee that it will and you should not depend on that behavior.
Essentially think of a std::string
in these implementations as being similar-ish to:
class string {
// space for small strings; only used if _external is nullptr
char _local[16];
// pointer to heap-allocated memory; if nullptr, the string is stored in _local
char* _external;
// stores a string using small-string optimization is appropriate
void store(char* src) {
if (std::strlen(src) < sizeof(_local)) {
// small string, store in our local buffer and set _external to nullptr
std::strcpy(_local, src);
delete _external;
_external = nullptr;
} else if (src != _external) {
// large string, allocate space in heap
delete _external;
_external = new char[std::strlen(src) + 1];
std::strcpy(_external, src);
}
}
public:
// assign a C string to this string
string(char* src) : _external(nullptr) { store(src); }
string& operator=(char* src) { store(src); return *this; }
// be sure to properly use either _local or _external pointer
char* c_str() { return _external ? _external : _local; }
bool empty() const { return c_str()[0] == 0; }
};
Upvotes: 1
Reputation: 154007
Formally, as jrok has pointed out, what you are doing is
undefined behavior, so you have no right to complain, no matter
what the implementation does. Practically, it will depend on
the implementation. The standard doesn't require the move
constructors of std::string
to do anything with regards to the
source string. If the implementation uses the small string
optimization (as does VC++, for example), and the string is
small enough to qualify, then there's no way it can do anything
but memcpy the characters; they are in a char[]
in the
string object itself. A CoW implementation (like that in g++) might
not do anything different that what it does in the copy
constructor either, on the grounds that it's not worth the
bother; you won't really save much. (But it might, since just
swapping the pointers would save a little.)
I just tried the same thing in VC++, but with a string of 100 characters. It did the swap (and the two pointers were equal). So the behavior of VC++ depends on whether the string is small enough for the SSO to apply. G++ swaps in all cases.
Upvotes: 9
Reputation: 45450
Visualtudio compares the length of the string before it really moves the pointer, by debugging into std::move
code, it shows it copies the string of the length is under 16 bytes
void _Assign_rv(_Myt&& _Right)
{
// assign by moving _Right
if (_Right._Myres < this->_BUF_SIZE) //_BUF_SIZE is 16, comment mine
_Traits::move(this->_Bx._Buf, _Right._Bx._Buf,
_Right._Mysize + 1);
else
{ // copy pointer
this->_Getal().construct(&this->_Bx._Ptr, _Right._Bx._Ptr);
_Right._Bx._Ptr = pointer();
}
this->_Mysize = _Right._Mysize;
this->_Myres = _Right._Myres;
_Right._Tidy();
}
Upvotes: 3