在 unordered_map 中使用 string_view 作为键的安全方法

Safe way to use string_view as key in unordered map

我的类型 Val 包含 std::string 键。

struct Val
{
std::string thekey;
float somedata;
}

我想将我的类型放入无序映射中,以键为键。出于内存和避免转换的原因,我希望将 std::string_view 作为键类型。是否可以在使用 unique_ptr 时创建指向 val.thekey 的密钥?

std::unique_ptr<Val> valptr = ...;
std::unordered_map<std::string_view,std::unique_ptr<Val>> themap;
themap[std::string_view(valptr->thekey)] = std::move(valptr); // is this ok and safe?

Safe way to use string_view as key in unordered map

一般没有,因为视图下的存储可能随时更改,使您的地图不变量失效。

关联容器通常拥有一个const键正是为了避免这种情况。

在您的特定情况下std::unordered_set<Val, ValKeyHash, ValKeyEqual>与合适的散列和相等仿函数一起使用更有意义。


编辑,这些合适的仿函数就是

struct ValKeyHash {
    std::size_t operator() (Val const &v)
    {
        return std::hash<std::string>{}(v.thekey);
    }
};

struct ValKeyEqual {
    bool operator() (Val const& a, Val const& b)
    {
        return a.thekey == b.thekey;
    }
};

显然,这让我们有点不满意使用临时 Val{key, dummy_data} 进行查找的要求,至少在我们可以在其他答案中使用 C++20 transparent/projected 版本之前是这样。

,你应该这样做

namespace utils {
  // adl hash function:
  template<class T>
  auto hash( T const& t )
  ->decltype( std::hash<T>{}(t) )
  { return std::hash<T>{}(t); }

  // Projected hasher:
  template<class Proj>
  struct ProjHash {
    template<class T>
    constexpr std::size_t operator()(T const& t)const {
      return hash(Proj{}(t));
    }
    using is_transparent=std::true_type;
  };

  // Projected equality:
  template<class Proj>
  struct ProjEquals {
    template<class T, class U>
    constexpr std::size_t operator()(T const& t, U const& u)const {
      return std::equal_to<>{}( Proj{}(t), Proj{}(u) );
    }
    using is_transparent=std::true_type;
  };
}

// A projection from Val to a string view, or a string view
// to a string view:
struct KeyProj {
  std::string_view operator()(Val const& val) const { return val.thekey; }
  std::string_view operator()(std::string_view sv) const { return sv; }
};

std::unordered_set<Val, ProjHash<KeyProj>, ProjEquals<KeyProj>> theset;

现在你可以

theset.find("hello")

查找集合中键为"hello"的元素。


地图在这里从根本上是错误的,因为地图具有上述设置所没有的功能是不正确的。像 mymap["hello"],如果找不到,它会创建一个 Val;我们现在在容器中有一个悬挂的字符串视图。

std 中的侵入式地图是带有投影的集合,而不是将值作为键的引用的地图。