Сколько объектов типа T существует в std::vector<T> сразу после резервирования памяти, если размер контейнера остаётся нулевым?
Существует ноль объектов типа T. Операция резервирования изменяет вместимость и выделяет сырое хранилище, но не создаёт элементы и не вызывает их конструкторы.
Разделение size и capacity появилось как основа эффективного динамического массива. Контейнер может заранее получить запас памяти, чтобы последующие добавления не требовали частых перевыделений и перемещений уже существующих элементов.
Такой подход особенно важен для типов с дорогим конструированием или перемещением: резервируется память заранее, но сами объекты создаются только тогда, когда они действительно добавляются в контейнер.
После резервирования памяти доступный объём хранения может быть больше нуля, но это не означает, что по этим адресам уже существуют объекты. У std::vector<T> количество существующих элементов определяется size, а не capacity.
Обращение к позиции за пределами размера контейнера, даже если она находится внутри зарезервированной вместимости, некорректно. Память может быть выделена, но объект T, к которому выполняется обращение, ещё не создан; такое использование приводит к неопределённому поведению.
reserve(n) при необходимости выделяет сырое хранилище, достаточное как минимум для n элементов. Для пустого вектора после reserve(n) значение размера остаётся нулевым, поэтому конструктор T не вызывается ни разу.
Элемент появляется только после операции, которая создаёт его, например push_back или emplace_back. При этом size увеличивается на единицу, а уничтожение элемента при удалении выполняется только для реально существующих объектов.
resize(n), в отличие от reserve(n), изменяет размер и создаёт n элементов: для этого вызываются соответствующие конструкторы. Поэтому reserve выбирают для предотвращения перевыделений, а resize — когда элементы действительно должны существовать и быть доступны по индексам.
Нельзя считать зарезервированную область готовым массивом T и вручную размещать там элементы в обход интерфейса std::vector. Это нарушает инварианты контейнера: его размер не будет отражать число созданных объектов, а при уничтожении или перевыделении контейнер не сможет корректно управлять ими.
Сервис заранее получает оценку количества сообщений и хочет уменьшить число перевыделений при накоплении результата. Вариант с reserve выделяет память заранее, но создаёт каждый объект только при фактическом добавлении. Это обычно лучший выбор, если сообщения имеют дорогое конструирование.
Вариант с resize создаёт все элементы немедленно. Он подходит, если каждая позиция должна существовать заранее и затем заполняться через присваивание, но может привести к лишним конструкциям и инициализациям.
Хранение сырого буфера с последующим ручным временем жизни объектов даёт больший контроль, однако требует самостоятельно соблюдать правила выравнивания, конструирования, уничтожения и обработки исключений. Поэтому для обычного динамического массива выбирают reserve вместе с emplace_back: результатом становится меньший объём ручного управления и корректное владение памятью.
reserve конструкторы элементов?Нет, если речь идёт о новой вместимости без изменения размера. reserve может переместить уже существующие элементы при перевыделении, но не создаёт дополнительные элементы до значения capacity.
Для пустого вектора конструкторы элементов не вызываются вообще. Выделяется только хранилище, которым контейнер сможет воспользоваться позднее.
capacity, но не меньше size?Нельзя. Проверка границ у operator[] концептуально относится к размеру контейнера, а не к его вместимости. За пределами size нет существующего элемента T, поэтому попытка чтения или записи через такой индекс имеет неопределённое поведение.
Если элементы должны существовать, нужно создать их через resize, push_back или emplace_back. Одна только предварительно выделенная память не превращается в объекты автоматически.
reserve и считать его элементом вектора?Потому что создание объекта в выделенной памяти не изменяет внутренний размер std::vector. Контейнер не будет знать об этом объекте, не уничтожит его в обычном цикле очистки и может перезаписать или потерять его при перевыделении.
Для низкоуровневого ручного управления используют отдельное сырое хранилище и явные операции времени жизни либо специализированные аллокаторные механизмы. В обычном std::vector создание элементов должно выполняться через его операции, чтобы структура контейнера и фактическое время жизни объектов оставались согласованными.