有没有理由在C++ 1z中使用std :: map :: emplace()而不是try_emplace()?

s3r*_*vac 31 c++ c++17

在C++ 17,std::mapstd::unordered_map有一个新的成员函数模板:try_emplace().在n4279中提出的这一新增功能表现相似emplace(),但具有以下优点:

  • try_emplace()如果插入没有发生,则不会从rvalue参数移动.这在操作其值为仅移动类型的地图时非常有用,例如std::unique_ptr.
  • try_emplace()分别处理密钥和参数mapped_type,这使得它比以value_type(即std::pair)表示的通用变更器更直观.

鉴于上述优点,在编写仅使用C++ 1z的代码时,您是否会使用emplace()C++ 11而不是try_emplace()C++ 1z?

Pra*_*ian 30

try_emplace确实可以取代大多数用途emplace,但是如果你有map一个不可复制和不可移动密钥类型的异常用例,try_emplace将无法工作,因为它复制或移动密钥.在这种情况下,你必须使用emplace具有std::pair分段施工构造,以避免拷贝和移动.

即使您的密钥类型是可复制和/或可移动的,分段构造也是避免复制或移动构造密钥的唯一方法,因此可能存在您喜欢的情况try_emplace.


T.C*_*.C. 10

try_emplace 也不支持异构查找 - 它不能,因为它需要密钥.

假设我们有一个std::map<std::string, int, std::less<>> counts;和一个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);
}
Run Code Online (Sandbox Code Playgroud)


Har*_*ngh 5

我总是喜欢try_emplace over 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";
    }
}
Run Code Online (Sandbox Code Playgroud)

对于上面的代码

m.try_emplace(b.country, b, 1);
Run Code Online (Sandbox Code Playgroud)

如果插入不成功,则不会构造对,这会增加性能