Как работает boost:: unordered_map.emplace(Args &&... args)?

В соответствии с documentation:

Вставляет объект, построенный с помощью аргументы args, в контейнере тогда и только тогда, когда в контейнер с эквивалентным ключом.

Но единственные объекты, которые могут быть вставлены в unordered_map, имеют тип std::pair<Key const, Mapped> (потому что для вставленного объекта необходимы как ключ, так и значение), который, как известно, принимает конструктор с ровно двумя аргументами. Так почему же он использует форму вариационной функции? Наверняка есть кое-что, что я совершенно не понимаю об этом.

Ответ 1

См. эту статью SO на emplace_back по сравнению с push_back. По сути, он позволяет создавать объект из переданных в него аргументов без необходимости создания объекта, который должен быть передан первым. Это экономит накладные расходы, удаляя построение копии, которое обычно происходит в результате создания объектов, которые нужно вставить.

Итак, вы можете уйти от этого:

unordered_map<int,int> foo;
foo.emplace(4, 5);

вместо

foo.insert(std::make_pair(4, 5));

Еще лучше (и если я не ошибаюсь), вы можете пройти этот маршрут:

struct Bar{
    int x,y;
    Bar(int _x, int _y) : x(_x), y(_y){}
};

unordered_map<int,Bar> baz;
baz.emplace(4, 5, 6);

И взято из Wiki на С++ 0x:

В связи с характером формулировки ссылок rvalue и некоторой модификацией формулировок для ссылок на lvalue (регулярные ссылки), ссылки rvalue позволяют разработчикам обеспечивать идеальную пересылку функций. В сочетании с вариативными шаблонами эта возможность позволяет создавать шаблоны функций, которые могут идеально пересылать аргументы другой функции, которая принимает эти конкретные аргументы. Это наиболее полезно для параметров конструктора пересылки, для создания функций factory, которые автоматически вызывают правильный конструктор для этих конкретных аргументов.

Что работает следующим образом:

template<typename TypeToConstruct> struct SharedPtrAllocator {

    template<typename ...Args> std::shared_ptr<TypeToConstruct> construct_with_shared_ptr(Args&&... params) {
        return std::shared_ptr<TypeToConstruct>(new TypeToConstruct(std::forward<Args>(params)...));
    }
}

Опять же, бесстыдно украден из статьи Wiki, упомянутой выше.

Ответ 2

Теперь, когда стандартная библиотека С++ интегрировала ту часть Boost:

Из http://en.cppreference.com

#include <iostream>
#include <utility>
#include <tuple>

#include <unordered_map>

int main(){
    std::unordered_map<std::string, std::string> m;

    // uses pair piecewise constructor
    m.emplace(std::piecewise_construct,
        std::forward_as_tuple("c"),
        std::forward_as_tuple(10, 'c'));

    for (const auto &p : m) {
        std::cout << p.first << " => " << p.second << '\n';
    }
}

std::piecewise_construct - это константа, которая не оставляет двусмысленности в отношении того, как будут использоваться аргументы

  • Первый кортеж будет использоваться для создания ключа
  • Второй для построения значения