Что требуется, чтобы реаллокация vector использовала move-семантику?

«Что требуется, чтобы реаллокация vector использовала move-семантику?» — вопрос из категории STL, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для того чтобы std::vector использовал move-семантику вместо копирования при реаллокации (например, при увеличении capacity), необходимо выполнение двух условий:

  1. Наличие небросающего move-конструктора. Конструктор перемещения типа элемента должен быть объявлен с спецификацией noexcept (или быть тривиальным).
  2. Отсутствие пользовательского 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 для понимания логики выбора компилятора. Если ваш тип не удовлетворяет условиям, реаллокация будет происходить медленнее из-за операций копирования.