2011-01-29 4 views
11

Согласно documentation этого:Как работает boost :: unordered_map.emplace (Args && ... args)?

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

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

ответ

8

См. Статью this 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 по C++0x:

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

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

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)...)); 
    } 
} 

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

+0

Последний пример кажется странным для меня. Предположим, что у нас есть 2 конструктора для 'Key':' Key (int) 'и' Key (int, int) 'и 2 для' Value': 'Value (int, int)' и 'Value (int)', как компилятор собирается disambuiguate в случае создания пары «ключ/значение»? –

+0

Прежде всего, я сначала беру? Поэтому он создает 'X (int, int)' first, а затем 'X (int)'. Будет проверять его позже и отправить обратно. Может также просто не компилироваться и вызывать «неоднозначную» ошибку времени компиляции. – Xeo

+0

@ Matthieu M. Идея, на самом деле. Проблема в том, что спецификация в настоящее время не полностью формализована, и поэтому мы не можем точно знать, как этот тип вопроса должен отвечать до тех пор, пока он не будет. Нам нужен адвокат языка, чтобы прийти и разобраться в каких-то двусмысленностях моего ответа. Я проголосую за это. – wheaties

5

Теперь, когда ++ Стандартная библиотека C интегрировал, что часть повышающего:

От 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's 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 является константой, которая не оставляет двусмысленности о том, как аргументы будут использованы

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