С++: Stack push() vs emplace()

Попытка понять разницу между использованием push() или emplace() для std::stack.

Я думал, что если я создам std::stack<int>, тогда я бы использовал push(), потому что целое является примитивным типом, и для emplace() нечего строить.

Однако, если я создавал std::stack<string>, тогда я бы выбрал emplace(), потому что std::string - это объект.

Это правильное использование?

Ответ 1

Чтобы полностью понять, что делает emplace_back, нужно сначала понять вариативные шаблоны и ссылки rvalue.

Это довольно продвинутая и глубокая концепция в современном С++. На карте это будет обозначаться как "есть драконы".

Вы говорите, что вы новичок в С++ и пытаетесь изучить этот материал. Это может быть не тот ответ, который вы, возможно, ищете, но сейчас вы должны пропустить эту деталь и вернуться позже, после того, как вы завернули свой мозг вокруг вариативных шаблонов и ссылок на rvalue. Тогда все должно иметь смысл.

Но если вы настаиваете: для контейнера, содержащего простые, элементарные типы, такие как целые числа, мало, если есть разница. Разница возникает, когда тип контейнера представляет собой некоторый большой сложный класс со сложным конструктором и/или конструктором-копий.

Конечный результат push или emplace точно равен 100%, то же самое. Контейнер получает к нему еще один элемент. Разница заключается в том, откуда элемент:

1) push принимает существующий элемент и добавляет его копию в контейнер. Простой, понятный. push всегда принимает ровно один аргумент, элемент для копирования в контейнер.

2) emplace создает другой экземпляр класса в контейнере, который уже добавлен в контейнер. Аргументы emplace передаются как аргументы конструктору класса контейнера. Emplace может иметь один аргумент, более одного аргумента или вообще никакого аргумента, если класс имеет конструктор по умолчанию.

Обратите внимание, что когда конструктор класса принимает один аргумент, он может злоупотреблять push и передавать ему аргумент конструктора вместо существующего экземпляра класса. Но пусть притворяется, что этот вариант не существует, он часто приводит к ужасной производительности кода, особенно с нетривиальными классами.

Итак: если вы хотите добавить копию существующего экземпляра класса в контейнер, используйте push. Если вы хотите создать новый экземпляр класса с нуля, используйте emplace.

Ответ 2

Если у вас есть vector<X>, то emplace_back(a, b, c) строит объект X внутри вектора.

В отличие от этого, push_back(X(a, b, c)) сначала создает временное, которое затем перемещается в вектор.