В случае libc ++ конструктор перемещения строк очищает исходный код, но это не является ненужным.Действительно, автором этой строковой реализации был тот же человек, который руководил предложением семантики перемещения для C ++ 11.; -)
Эта реализация строки libc ++ на самом деле была разработана из членов перемещения наружу!
Вот код с некоторыми ненужными деталями (например, режим отладки):
template <class _CharT, class _Traits, class _Allocator>
basic_string<_CharT, _Traits, _Allocator>::basic_string(basic_string&& __str)
_NOEXCEPT
: __r_(_VSTD::move(__str.__r_))
{
__str.__zero();
}
В двух словах, этот код копирует все байты источника, а затем обнуляет все байты источника.Одно замечание: ветвления нет: этот код делает то же самое для длинных и коротких строк.
Режим длинных строк
В «длинном режиме»,макет состоит из 3 слов, указателя данных и двух целочисленных типов для хранения размера и емкости, минус 1 бит для флага long / short.Плюс пробел для распределителя (оптимизированный для пустых распределителей).
Таким образом, это копирует указатель / размеры, а затем обнуляет источник, чтобы освободить владельца указателя.Это также устанавливает источник в «короткий режим», так как короткий / длинный бит означает короткий в нулевом состоянии.Также все нулевые биты в коротком режиме представляют короткую строку нулевого размера, отличную от нуля.
Режим короткой строки
Если источником является короткая строка,код идентичен: байты копируются, а исходные байты обнуляются.В коротком режиме нет самореферентных указателей, поэтому копирование байтов является правильным алгоритмом.
Теперь верно, что в «коротком режиме» обнуление 3-х слов источника может кажется ненужным, но для этого нужно было бы проверить длинный / короткий бит и нулевые байты в длинном режиме.Выполнение этой проверки и перехода на самом деле обходится дороже, чем просто обнуление трех слов из-за случайного неправильного предсказания перехода (разрыв конвейера).
Вот оптимизированная сборка x86 (64-битная) для libc ++string
Конструктор перемещения.
std::string
test(std::string& s)
{
return std::move(s);
}
__Z4testRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE: ## @_Z4testRNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movq 16(%rsi), %rax
movq %rax, 16(%rdi)
movq (%rsi), %rax
movq 8(%rsi), %rcx
movq %rcx, 8(%rdi)
movq %rax, (%rdi)
movq $0, 16(%rsi)
movq $0, 8(%rsi)
movq $0, (%rsi)
movq %rdi, %rax
popq %rbp
retq
.cfi_endproc
(без ветвей!)
<aside>
Размер внутреннего буфера для короткой строки также оптимизирован дляпереместить участников.Внутренний буфер «объединен» с 3 словами, необходимыми для «длинного режима», так что sizeof(string)
не требует больше места, чем в длинном режиме.Несмотря на этот компактный sizeof
(наименьший из 3 основных реализаций), libc ++ обладает самым большим внутренним буфером на 64-битных архитектурах: 22 * 1039 *.
Маленький sizeof
означает более быстрое перемещение членов, поскольку всеэти члены делают копирование и нулевые байты макета объекта.
См. этот ответ Stackoverflow для получения дополнительной информации о размере внутреннего буфера.
</aside>
Сводка
Итак, в итоге, установка источника на пустую строку необходима в «длинном режиме» для передачи владения указателем, а также необходимо в коротком режиме по соображениям производительности, чтобы избежать прерванного конвейера.
Я не комментирую реализацию libstdc ++, так как я не писал этот код, и ваш вопрос в любом случае уже хорошо справляется с этой задачей.: -)