Есть ли причина использовать std:: map:: emplace() вместо try_emplace() в С++ 1z?
В С++ 17 std::map
и std::unordered_map
получили новый шаблон функции-члена: try_emplace()
. Это новое дополнение, предложенное в n4279, ведет себя аналогично emplace()
, но имеет следующие преимущества:
-
try_emplace()
не перемещается из аргументов rvalue, если вставка не происходит. Это полезно при работе с картами, значения которых являются типами только для перемещения, такими как std::unique_ptr
. -
try_emplace()
обрабатывает ключ и аргументы mapped_type
отдельно, что делает его несколько более интуитивным, чем универсальные мутаторы, которые выражаются в терминах value_type
(который является std::pair
).
Учитывая вышеперечисленные преимущества, вы когда-нибудь использовали бы emplace()
из С++ 11 вместо try_emplace()
из С++ 1z при написании кода только для С++ 1z?
Ответы
Ответ 1
try_emplace
действительно может заменить большинство применений emplace
, но если у вас есть необычный случай использования map
с типом не копируемого и неподвижного ключа, try_emplace
не будет работать, потому что он копирует или перемещает ключ. В этом случае вы должны использовать emplace
с std::pair
кусочным конструктором конструкции, чтобы избежать копирования и перемещения.
Даже если ваш тип ключа можно копировать и/или перемещать, кусочная конструкция - это единственный способ избежать копирования или перемещения конструирования ключа, поэтому могут быть случаи, когда вы предпочитаете, чтобы над try_emplace
.
Ответ 2
try_emplace
также не поддерживает гетерогенный поиск - он не может, поскольку он принимает ключ.
Предположим, что a std::map<std::string, int, std::less<>> counts;
и a std::string_view
sv
. Я хочу сделать эквивалент ++counts[std::string(sv)];
, но я не хочу создавать временный std::string
, который просто расточительный, особенно если строка уже присутствует на карте. try_emplace
не может вам помочь. Вместо этого вы бы сделали что-то вроде
if(auto lb = counts.lower_bound(sv); lb != counts.end() && lb->first == sv) {
++lb->second;
}
else {
counts.emplace_hint(lb, sv, 1);
}
Ответ 3
Я бы всегда предпочел try_emplace, а не emplace. Принципиальное отличие состоит в том, что try_emplace не будет создавать объект, связанный с ключом, если ключ уже существует. Это повысит производительность в случае, если создание объектов этого типа является дорогостоящим
Например, приведенный ниже код (пример с https://github.com/PacktPublishing/Cpp17-STL-Cookbook/blob/master/Chapter02/efficient_insert_or_reassign_to_map.cpp)
#include <iostream>
#include <functional>
#include <list>
#include <map>
using namespace std;
struct billionaire {
string name;
double dollars;
string country;
};
int main()
{
list<billionaire> billionaires {
{"Bill Gates", 86.0, "USA"},
{"Warren Buffet", 75.6, "USA"},
{"Jeff Bezos", 72.8, "USA"},
{"Amancio Ortega", 71.3, "Spain"},
{"Mark Zuckerberg", 56.0, "USA"},
{"Carlos Slim", 54.5, "Mexico"},
// ...
{"Bernard Arnault", 41.5, "France"},
// ...
{"Liliane Bettencourt", 39.5, "France"},
// ...
{"Wang Jianlin", 31.3, "China"},
{"Li Ka-shing", 31.2, "Hong Kong"}
// ...
};
map<string, pair<const billionaire, size_t>> m;
for (const auto &b : billionaires) {
auto [iterator, success] = m.try_emplace(b.country, b, 1);
if (!success) {
iterator->second.second += 1;
}
}
for (const auto & [key, value] : m) {
const auto &[b, count] = value;
cout << b.country << " : " << count << " billionaires. Richest is "
<< b.name << " with " << b.dollars << " B$\n";
}
}
Для приведенного выше кода
m.try_emplace(b.country, b, 1);
если будет неудачная вставка, пары не будут созданы, что повышает производительность