Pull to refresh
1
0.1
Send message
  • push_back создает объект, затем перемещает/копирует его в вектор

  • emplace_back создает объект прямо в векторе (без копирования/перемещения)

push_back сам по себе ничего не создает. Пример из статьи корректен, но лишний вызов конструктора происходит на стороне, вызывающей push_back.

std::vector<Point> v;
v.reserve(10);
// чтобы избежать реаллокаций
// push_back: конструктор + move
v.push_back(Point(1, 2));
// Вывод:
// Constructor
// Move constructor
// emplace_back: только конструктор
v.emplace_back(3, 4);
// Вывод:
// Constructor

Для уже существующих объектов разницы не будет

std::vector<Something> v;
Something v1, v2;
v.push_back(v1); // copy
v.push_back(std::move(v1)); // move
v.emplace_back(v2); // copy
v.emplace_back(std::move(v2)); // move

-------------------------------

Что возвращают:

  • push_backvoid (до C++17), reference (C++17+)

  • emplace_backvoid (до C++17), reference (C++17+)

push_back не возвращает reference.

reserve(): предварительное выделение памяти

Когда НЕ использовать:

  • ❌ Работаете с огромными объектами и хотите экономить память

Не очень ясно, как неиспользование 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);
}

Тут на самом деле нет разницы между первым и последним примером - и там и там копирование всего вектора.

Рекомендую автору перестать копировать ответы из llm и проверять статьи перед публикацией на предмет корректности содержимого.

Гарантии безопасности исключений

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 может быть поврежден
}

Information

Rating
3,868-th
Registered
Activity