【问题标题】:unordered_map insert on past-the-end iteratorunordered_map 在过去的迭代器上插入
【发布时间】:2019-10-26 12:30:16
【问题描述】:

在没有找到key的情况下,通过find返回的pass-the-end迭代器进行插入是否定义且有效:

auto it = m.find(key);
if (it == m.end()) {
    m.insert(it, make_pair(key, value));
}

因为与使用相比,这将节省额外的查找:

m[key] = value;

【问题讨论】:

  • 保存查找的原因是什么? m[key] 将返回对现有元素或它刚刚创建的值初始化元素的引用。
  • 传递给insert的迭代器只是一个提示。实现可以随意忽略它。如果使用了提示并且提示错误,则将执行常规插入。也许你应该看看try_emplace
  • 查看实现,libc++ 和 MSVCRT 都完全忽略了对 insert 的迭代器提示。
  • 好的,那我可以用it->first = key; it->second = value吗?
  • @fluter,不,你不能假装一个过去的迭代器指向一个实际的对象。

标签: c++ c++11 iterator unordered-map


【解决方案1】:

虽然将结束迭代器作为提示传递给unordered_map::insert 是安全的,但它实际上并没有完成任何事情。

在三个主要的标准库实现中,只有 libstdc++ 使用该提示做任何事情,即使这样,它也只会在指向有效条目时才使用它。

如果您想避免进行两次查找(一个确定元素是否存在,另一个确定元素是否存在),您应该尝试插入它。 insert 既返回一个 bool 表示是否插入了新元素,又返回一个指向新插入元素或阻止插入的现有元素的迭代器。这意味着在元素不存在时插入元素并获取元素的迭代器的最有效方法是执行以下操作:

decltype(m)::iterator it;
bool inserted;
std::tie(it, inserted) = m.insert(std::make_pair(key, value));
if (inserted) {
    // ...
}

如果您的 mapped_type 构建起来很昂贵,您可以避免使用 try_emplace 构建它(仅适用于 C++17 或更高版本):

auto [it, inserted] = m.try_emplace(key, args, to, value, constructor);
if (inserted) {
    // ...
}

C++17之前,你可以让operator[]默认构造元素并比较容器大小来判断是否添加了新元素:

size_t size_before = m.size();
ValueType& element = m[key];
size_t size_after = m.size();
if (size_before != size_after) {
    element = ValueType{args, to, value, constructor};
    // ...
}

显然,这具有默认构造元素并且仅使用可分配类型的缺点。

【讨论】:

    猜你喜欢
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-30
    • 1970-01-01
    • 2019-04-19
    相关资源
    最近更新 更多