Это позволяет избежать создания ненужных или потенциально невозможных копий объектов.
Сначала давайте рассмотрим тип значения, отличный от std::string
. Я буду использовать что-то, что не может быть скопировано, но это также относится к вещам, которые можно скопировать, но для которых это дорого:
struct NonCopyable
{
NonCopyable(int a, char b) {} // Dummy
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
Как мы можем вставить что в std::map<int, NonCopyable> m
? Давайте go через возможности:
m.insert({10, NonCopyable{10, 'a'}});
Этот не будет работать . Он принимает ссылку на std::pair
и копирует ее, что требует копирования объекта NonCopyable
, что невозможно.
m.emplace(10, NonCopyable{10, 'a'}});
Это также не будет работать . Хотя он создает значение std::pair
на месте, он все равно должен скопировать объект NonCopyable
.
m.emplace(std::piecewise_construct,
std::tuple{10},
std::tuple{10, 'a'});
Наконец, то, что работает . Элемент std::pair
создается на месте, как и его два подобъекта.
Но давайте рассмотрим другую ситуацию. Рассмотрим этот класс:
struct UsesNonCopyable
{
UsesNonCopyable(const NonCopyable&) {}
UsesNonCopyable(const UsesNonCopyable&) = delete;
UsesNonCopyable& operator=(const UsesNonCopyable&) = delete;
};
Теперь, как мы можем добавить элементы в std::map<int, UsesNonCopyable> m
?
Первые два параметра выше не будут работать по той же причине, что и в предыдущий случай, но внезапно ни третий не будет:
m.emplace(std::piecewise_construct,
std::tuple{10},
std::tuple{NonCopyable{10, 'a'}});
Этот не будет работать , потому что NonCopyable
объект должен быть скопирован в std::tuple
объект, который передается конструктору std::pair
.
Здесь std::forward_as_tuple
входит:
m.emplace(std::piecewise_construct,
std::tuple{10},
std::forward_as_tuple(NonCopyable{10, 'a'}));
Это работает , потому что теперь вместо передачи m.emplace
кортеж, содержащий копию объекта NonCopyable
, мы используем std::forward_as_tuple
для создания кортежа, который содержит ссылку на объект NonCopyable
. Эта ссылка передается конструктору std::pair
, который, в свою очередь, перенаправляет его в конструктор UsesNonCopyable
.
Обратите внимание, что большая часть этого усложнения устранена с помощью C ++ 17 добавление std::map::try_emplace
, пока ваш тип ключа копируемый. Следующая будет работать просто отлично и значительно проще:
std::map<int, UsesNonCopyable> m;
m.try_emplace(10, NonCopyable{10, 'a'});