Ответ 1
Вы, вероятно, видите эффект оптимизации маленькой/короткой строки. Чтобы избежать ненужных выделений для каждой крошечной строки, многие реализации std::string
включают в себя небольшой массив фиксированного размера для хранения небольших строк, не требуя new
(этот массив обычно повторно использует некоторые другие члены, которые не нужны, когда динамическое выделение имеет не использовался, поэтому он потребляет мало или вообще никакой дополнительной памяти для его предоставления, как для маленьких, так и для больших string
), и эти строки не получают преимущества от std::move
(но они маленькие, так что это нормально). Большие строки потребуют динамического выделения и передадут указатель так, как вы ожидаете.
Просто для демонстрации, этот код на g++
:
void move_test(std::string&& s) {
std::string s2 = std::move(s);
std::cout << "; After move: " << std::hex << reinterpret_cast<uintptr_t>(s2.data()) << std::endl;
}
int main()
{
std::string sbase;
for (size_t len=0; len < 32; ++len) {
std::string s1 = sbase;
std::cout << "Length " << len << " - Before move: " << std::hex << reinterpret_cast<uintptr_t>(s1.data());
move_test(std::move(s1));
sbase += 'a';
}
}
генерирует высокие (стековые) адреса, которые изменяются в структуре перемещения на длину 15 или меньше (предположительно, зависит от размера указателя архитектуры), но переключаются на низкие (куча) адреса, которые остаются неизменными после конструкции перемещения после достижения длины 16 или выше (переключатель в 16, а не в 17, потому что это NUL
-terminating строк, так как C++ 11 и выше требуют этого).
Чтобы быть на 100% ясным: это деталь реализации. Ни одна часть спецификации C++ не требует такого поведения, поэтому вы не должны полностью полагаться на то, что это происходит, и когда это происходит, вы не должны полагаться на то, что это происходит для определенных длин строк.