❌ Работаете с огромными объектами и хотите экономить память
Не очень ясно, как неиспользование reserve поможет экономить память, учитывая экспоненциальную стратегию роста при использовании push_back/emplace_back.
-------------------------------
1. Избегайте ненужных копирований
// ❌ Плохо: копирование при каждой вставке
std::vector<std::string> v;
for (const auto& s : data) {
v.push_back(s); // копирование
}
// ✅ Хорошо: перемещение
std::vector<std::string> v;
for (auto&& s : data) {
v.push_back(std::move(s)); // перемещение
}
// ✅ Еще лучше: emplace_back (если подходит)
std::vector<std::string> v;
for (const auto& s : data) {
v.emplace_back(s);
}
Тут на самом деле нет разницы между первым и последним примером - и там и там копирование всего вектора.
std::vector предоставляет строгую гарантию (strong exception guarantee) для большинства операций:
Если операция бросает исключение, вектор остается в исходном состоянии
НО: только если конструктор перемещения T помечен noexcept!
Ну это уже совсем бред. Буквально несколько абзацев назад было написано, что вектор при реаллокации может копировать или перемещать элементы в зависимости от noexcept'ности конструктора перемещения хранимого типа. Спойлер - это сделано именно для того чтобы обеспечить strong exception safety guarantee для типов, у которых move-constructor не noexcept. (Там есть несколько нюансов про типы, которые не CopyInsertable, но это совсем частные случаи. Кстати, было бы круто, если бы эти нюансы упомянули в статье под названием "std::vector: от основ до тонкостей реализации").
Соответственно, в приведенном примере вообще не будет вызываться перемещающий конструктор. так что с вектором все будет нормально.
struct ThrowingType {
ThrowingType() = default;
ThrowingType(const ThrowingType&) = default;
// ❌ Может бросить исключение
ThrowingType(ThrowingType&&) {
throw std::runtime_error("Move failed!");
}
};
std::vector<ThrowingType> v(10);
try {
v.reserve(20); // может привести к неконсистентному состоянию!
} catch (...) {
// v может быть поврежден
}
push_back сам по себе ничего не создает. Пример из статьи корректен, но лишний вызов конструктора происходит на стороне, вызывающей push_back.
Для уже существующих объектов разницы не будет
-------------------------------
push_back не возвращает reference.
Не очень ясно, как неиспользование reserve поможет экономить память, учитывая экспоненциальную стратегию роста при использовании push_back/emplace_back.
-------------------------------
Тут на самом деле нет разницы между первым и последним примером - и там и там копирование всего вектора.
Рекомендую автору перестать копировать ответы из llm и проверять статьи перед публикацией на предмет корректности содержимого.
Ну это уже совсем бред. Буквально несколько абзацев назад было написано, что вектор при реаллокации может копировать или перемещать элементы в зависимости от noexcept'ности конструктора перемещения хранимого типа. Спойлер - это сделано именно для того чтобы обеспечить strong exception safety guarantee для типов, у которых move-constructor не noexcept. (Там есть несколько нюансов про типы, которые не CopyInsertable, но это совсем частные случаи. Кстати, было бы круто, если бы эти нюансы упомянули в статье под названием "std::vector: от основ до тонкостей реализации").
Соответственно, в приведенном примере вообще не будет вызываться перемещающий конструктор. так что с вектором все будет нормально.