Ответ
Для того чтобы std::vector использовал move-семантику вместо копирования при реаллокации (например, при увеличении capacity), необходимо выполнение двух условий:
- Наличие небросающего move-конструктора. Конструктор перемещения типа элемента должен быть объявлен с спецификацией
noexcept(или быть тривиальным). - Отсутствие пользовательского copy-конструктора или copy-присваивания. Наличие пользовательских копирующих операций может заставить компилятор не генерировать move-операции, что также приведет к копированию.
Почему это важно? std::vector должен обеспечивать строгую гарантию безопасности исключений. Если в процессе реаллокации move-конструктор выбросит исключение, вектор не сможет откатить состояние к исходному. Поэтому, если move-конструктор не помечен как noexcept, вектор перестраховывается и использует копирование, которое, как правило, также обеспечивает строгую гарантию.
Пример:
class MyType {
public:
// Ключевой момент: конструктор перемещения объявлен как noexcept
MyType(MyType&& other) noexcept {
data_ = other.data_;
other.data_ = nullptr;
}
// ... другие члены, но НЕТ пользовательского копирования
private:
int* data_;
};
int main() {
std::vector<MyType> vec;
vec.reserve(1);
vec.push_back(MyType{}); // Первый элемент
// При добавлении второго элемента capacity станет недостаточной,
// произойдет реаллокация с использованием move-семантики.
vec.push_back(MyType{});
}
Практическая проверка: Можно использовать std::move_if_noexcept для понимания логики выбора компилятора. Если ваш тип не удовлетворяет условиям, реаллокация будет происходить медленнее из-за операций копирования.