避免在map / unordered_map中进行多次查找

时间:2018-02-06 20:28:31

标签: c++ performance dictionary stdmap

假设我们有一个昂贵的函数映射stringint,并希望将结果缓存到地图中。

最简单的代码是

int mapStringToIntWithCache(std::string const& s) {
    static std::unordered_map<std::string, int> cache;
    if (cache.count(s) > 0) return cache[s];
    else return cache[s] = myExpensiveFunction(s);
}

但这有两次查找。

因此我倾向于写这个

int mapStringToIntWithCache(std::string const& s) {
    static std::unordered_map<std::string, int> cache;
    size_t sizeBefore = cache.size();
    int& val = cache[s];
    if (cache.size() > sizeBefore) val = myExpensiveFunction(s);
    return val;
}

这只有一次查找,但看起来有点笨拙。还有更好的方法吗?

2 个答案:

答案 0 :(得分:7)

只需使用(\w+)(?:\:)(.*), $1:$1 方法:

std::map::emplace()

答案 1 :(得分:1)

请注意@Slava的答案:如果你通过const左值引用传递参数,那么如果它是rvalue,你就不能从这个参数移动:

int i = mapStringToIntWithCache("rvalue argument here");

如果插入std::string,则临时cache参数将复制

你可以使用完美转发,但是,如果你想保持参数只有std::string类型(例如,对于字符串文字的隐式转换),那么你需要一些< em> wrapper-helper function 解决方案:

template <typename T>
int mapStringToIntWithCacheHelper(T&& s) {
  static std::unordered_map<std::string, int> cache;
  auto pair = cache.emplace( std::forward<T>(s), 0 );
  if( pair.second )
    pair.first->second = myExpensiveFunction(pair.first->first); // can't use s here !!!
  return pair.first->second;
}

int mapStringToIntWithCache(const std::string & s) {
  mapStringToIntWithCacheHelper(s);
}

int mapStringToIntWithCache(std::string && s) {
  mapStringToIntWithCacheHelper(std::move(s));
}