2016-11-28 2 views
3

Два способа emplacing:устанавливать (станд :: ход (ключ), станд :: ход (значение)) против (станд устанавливать :: make_pair (ключ, значение))

std::unordered_map<std::string, std::string> m; 

Первое: перемещаемый устанавливать ключ и значение

// 1. 
{ 
    std::string k1 = "key1"; 
    std::string v1 = "value1"; 
    m.emplace(std::move(k1), std::move(v1)); 
} 

Второе: с парой устанавливать сделал мой std::make_pair:

// 2. 
{ 
    std::string k2 = "key2"; 
    std::string v2 = "value2"; 
    m.emplace(std::make_pair(k2, v2)); 
} 

Что лучше (это означает более высокую скорость эффективной)?

+1

Обратите внимание, что 2. приблизительно эквивалентно 'm.insert (std :: make_pair (k2, v2));' как 'insert' перегружается, чтобы принять ссылку r-value. –

+2

Они семантически разные. Первый может оставить недопустимыми 'k1' и' k2'. Вы не должны полагаться на свои значения после 'std :: move'. Во-вторых, они оставят их действительными, так как они являются lvalues ​​и аргументами типа 'std :: make_pair' будет разрешено' std :: string & '. Причина в том, что 'std :: make_pair' принимает так называемые [универсальные ссылки] (https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers), то есть ссылки rvalue в контекст вывода типа. – Sergey

+0

Я думаю, что строки «k1' и' v1 »перемещены или нет - это отвлечение от того, что в противном случае было бы хорошим вопросом. В обоих случаях вы можете переместить строки или нет (и с помощью SSO это будет иметь мало значения для производительности). Я думаю, что было бы лучше сравнивать подобное. –

ответ

2

Что лучше?

Лучше как? Они не эквивалентны.

Пример 1 вызывает конструктор перемещения для k1 и v1, оставляя их в действительном, но в другом состоянии, их содержимое будет удалено. Если вы намереваетесь использовать k1 или v1, тогда нет опции, и вы должны использовать # 2. Если вы этого не сделаете, то вы также должны двигаться в # 2, чтобы избежать копию:

m.emplace(std::make_pair(std::move(k2), std::move(v2))); 

Который затем оставляет его до читаемости:

m.emplace(std::move(k1), std::move(k2)); 

Это вызывает std::pair «s шаблон конструктор для построения std::pair из 2 rvalue refs.

против

m.emplace(std::make_pair(std::move(k2), std::move(v2))); 

Это вызывает перемещение конструктор std::pair «s, так что это Rvalue.

Если «« Лучше? »« Вы имеете в виду производительность, тогда есть только один способ узнать, что является бенчмаркингом. Хотя я думаю, что m.emplace(std::move(k1), std::move(k2)); имеет небольшое преимущество. (Как отмечено в комментариях, поскольку он позволяет избежать преобразования из неконстантных на константные)


Главная точка, не переживайте, оба из них будут действительно эффективными, как все это движется. Выберите тот, который, по вашему мнению, более читабельен.

+2

Какое небольшое преимущество? – arturx64

+2

Но с 'make_pair' существует также преобразование из' pair 'to' pair '. Это может быть иначе, если вы используете 'std :: unordered_map :: value_type' конструктор вместо' make_pair'. – j6t

+0

j6t Вы правы. std :: pair следует избегать дополнительной конструкции. – arturx64

0

Второй вариант требует дополнительных операций перемещения/копирования, что не является хорошим. Первый вариант работает быстрее, потому что нет явного создания пары. Фактически, вставка и размещение с явным созданием std::pair дают вам такую ​​же скорость.
Я протестировал. Для опции std::make_pair требуется 3 вызова конструктора перемещения для ключа/значения, для опции с std::move требуется только 1 вызов конструктора перемещения. Пример без и с оптимизацией/Ox.MSVC-2012

class C 
{ 
    public: 
    C() 
    { 
     std::cout << "C()" << std::endl; 
    } 

    C(const C& c) 
    { 
     std::cout << "copy" << std::endl; 
    } 

    C(C&& c) 
    { 
     std::cout << "move" << std::endl; 
    } 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::unordered_map<int, C> c1; 

    std::cout << "---Test Move---" << std::endl; 
    C test1; 
    c1.emplace(1, std::move(test1)); 

    std::cout << "---Test std::make_pair---" << std::endl; 
    C test2; 
    c1.emplace(std::make_pair(2, test2)); 

    std::cout << "---Test bare pair---" << std::endl; 
    C test3; 
    c1.emplace(std::pair<const int, C>(3, test3)); 

    return 0; 
} 

--- Тест Move ---
C()
движение
--- Тест станд :: make_pair ---
C()
копия
движение
ход
--- Тест голая пара ---
C()
копия
движение

Смежные вопросы