首页 > 解决方案 > 在 std::unordered_map 中移动插入/放置失败后恢复移动的元素

问题描述

std::unordered_map我使用insertoremplace方法和移动语义填充 a 。当发生键冲突时,元素不会插入到地图中,但移动的元素无论如何都会被删除:

#include <unordered_map>
#include <iostream>

int main(){
    std::unordered_map<int, std::string> m;
    m.insert(std::make_pair<int, std::string>(0, "test"));
    std::string s = "test";
    
    // try insert
    auto val = std::make_pair<int, std::string>(0, std::move(s));
    if(m.insert(std::move(val)).second){
        std::cout << "insert successful, ";
    }else{
        std::cout << "insert failed, ";
    }
    std::cout << "s: " << s << ", val.second: " << val.second <<  std::endl;
    
    // try emplace
    s = "test";
    if(m.emplace(0, std::move(s)).second){
        std::cout << "emplace successful, ";
    }else{
        std::cout << "emplace failed, ";
    }
    std::cout << "s: " << s << std::endl;
    
    return 0;
}

输出:

insert failed, s: , val.second:
emplace failed, s: 

因此没有插入任何内容,但对象(示例中的字符串)无论如何都会被删除,因此无法将其用于任何其他目的。可能的修复方法是不使用移动语义或在插入/放置之前检查密钥,这两者都有性能损失。

恢复,我理解移动语义意味着移动的对象处于仅对销毁有用的状态,但我不完全理解为什么失败的移动操作std::unordered_map无论如何都会导致该状态,如果有任何允许避免这种情况而不会造成太多性能损失的代码模式。

标签: c++move-semanticsunordered-map

解决方案


如果可以选择使用 C++17 功能,try_emplace则仅当映射中不存在键时才会移动参数。

否则,您可以拥有自己的版本,通过组合findemplace(或insert)来获得(功能上)相同的效果。

请注意,如果它存在,这可能会比try_emplace实现效率低(因为如果键不在地图中,您将在容器中进行 2 次搜索)。


推荐阅读