Both libstdc++ and libc++ makes moved-from std::string
object empty, even if the original stored string is short and short string optimization is applied. It seems to me that this emptying makes an additional and unnecessary runtime overhead. For instance, here is the move constructor of std::basic_string
from libstdc++:
basic_string(basic_string&& __str) noexcept
: _M_dataplus(_M_local_data(), std::move(__str._M_get_allocator())) {
if (__str._M_is_local())
traits_type::copy(_M_local_buf, __str._M_local_buf, _S_local_capacity + 1);
else {
_M_data(__str._M_data());
_M_capacity(__str._M_allocated_capacity);
}
_M_length(__str.length());
__str._M_data(__str._M_local_data()); // (1)
__str._M_set_length(0); // (2)
}
(1) is an assignment that is useless in case of a short string, since data is already set to local data, so we just assign a pointer the same value it has been assigned before.
(2) Emptying string sets the string size and resets the first character in the local buffer, which, as far as I know, the Standard does not demand.
Usually, library implementers tries to implement the Standard as much efficient as it is possible (for instance, deleted memory regions are not zeroed-out). My question is if there might be any particular reasons why moved-from strings are emptied even if it is not required and it adds an unnecessary overhead. Which, can be easily eliminated, e.g., by:
basic_string(basic_string&& __str) noexcept
: _M_dataplus(_M_local_data(), std::move(__str._M_get_allocator())) {
if (__str._M_is_local()) {
traits_type::copy(_M_local_buf, __str._M_local_buf, _S_local_capacity + 1);
_M_length(__str.length());
}
else {
_M_data(__str._M_data());
_M_capacity(__str._M_allocated_capacity);
_M_length(__str.length());
__str._M_data(__str._M_local_data()); // (1)
__str._M_set_length(0); // (2)
}
}
std::string
was reference counted and all moved-from strings were empty. Old code might (incorrectly) depend on it. – Misdealoperator=
. – Lukelukens