Можно ли вызвать `emplace_back()` без параметров у `std::vector`?

Ответ

Да, можно, но только если тип элемента вектора (T) имеет конструктор по умолчанию.

Вызов vec.emplace_back(); эквивалентен созданию элемента с помощью T() и его размещению в конце вектора. Это работает благодаря perfect forwarding и вызову конструктора T с пустым списком аргументов.

Пример:

#include <vector>
#include <string>

int main() {
    std::vector<std::string> words;
    words.emplace_back(); // Создает пустую строку, вызывая std::string().
    // words теперь содержит: [""]

    std::vector<int> numbers;
    numbers.emplace_back(); // Создает int, инициализированный нулем (int()).
    // numbers теперь содержит: [0]
}

Что произойдет, если конструктора по умолчанию нет? Компиляция завершится ошибкой.

struct Point {
    int x, y;
    Point(int x, int y) : x(x), y(y) {} // Пользовательский конструктор
    // Конструктор по умолчанию Point() = default; не сгенерирован
};

std::vector<Point> points;
points.emplace_back(); // ОШИБКА КОМПИЛЯЦИИ:
// Нет подходящего конструктора для вызова 'Point::Point()'

Сравнение с push_back:

vec.push_back(T()); // Требует создания временного объекта, затем его перемещения/копирования.
vec.emplace_back(); // Создает объект напрямую в памяти вектора, избегая лишних операций.

Вывод: emplace_back() без аргументов — это эффективный способ добавить в вектор элемент, инициализированный значением по умолчанию, при условии, что такой конструктор существует.

Ответ 18+ 🔞

А, ну это же классика, ебать мои старые костыли! Смотри, тут всё просто, как три копейки.

Да, конечно, можно. Но с одной оговоркой, ёпта. Твой тип T внутри вектора должен уметь рождаться просто так, из ниоткуда, то есть иметь конструктор по умолчанию. Без этого — пиши пропало.

Когда ты пишешь vec.emplace_back();, это всё равно что крикнуть: «Эй, вектор, приготовь местечко на своём диванчике в конце и прямо там, на месте, роди мне новый T с помощью T()!». Вся магия в этом perfect forwarding, который передаёт пустой список аргументов прямо в конструктор.

Вот, смотри, как это выглядит вживую:

#include <vector>
#include <string>

int main() {
    std::vector<std::string> words;
    words.emplace_back(); // Просто вызывает std::string() и вуаля — пустая строка на месте.
    // Теперь в words болтается одна пустая строка: [""]

    std::vector<int> numbers;
    numbers.emplace_back(); // Вызывает int(), что даёт нам нолик.
    // numbers теперь: [0]
}

А что будет, если конструктора по умолчанию нет? Да хуй с горы — компилятор тебе такую ошибку влепит, что мало не покажется.

struct Point {
    int x, y;
    Point(int x, int y) : x(x), y(y) {} // Конструктор свой есть, а по умолчанию — нихуя.
    // Point() = default; — этой строчки нет, поэтому дефолтного рождения не будет.
};

std::vector<Point> points;
points.emplace_back(); // ОШИБКА, БЛЯДЬ!
// Компилятор орет: "Чел, я не знаю, как создать Point()! Где аргументы, мать твою?"

И главное, чем это лучше старого доброго push_back:

vec.push_back(T()); // Сначала создаёшь сироту-времянку где-то сбоку (T()), потом её толкаешь в вектор, заставляя копироваться или перемещаться. Лишняя работа.
vec.emplace_back(); // А это как по волшебству: память в векторе выделилась — и сразу в ней объект родился. Никаких лишних телодвижений. Красота!

Короче, вывод: emplace_back() без аргументов — это хитрая, но эффективная фишка, чтобы воткнуть в конец вектора элемент «с нуля». Работает на ура, если тип это позволяет. Если нет — ну, извини, придётся аргументы передавать.