【问题标题】:Safe way to use string_view as key in unordered map在 unordered_map 中使用 string_view 作为键的安全方法
【发布时间】:2021-10-22 14:44:13
【问题描述】:

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

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

我想把我的类型放在一个无序的映射中,以 key 作为 key。出于内存和避免转换的原因,我希望将 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?

【问题讨论】:

  • 这似乎是不需要的。如果您使用unordered_set,您可以创建自己的哈希器和比较器,只使用thekey,然后您可以节省拥有string_view 的成本。
  • themap[std::string_view(valptr-&gt;thekey)] = std::move(valptr); 如果密钥已经在地图中,则将不安全(旧的 std::string_view 将保留而旧的 Val 被破坏),除此之外,如果你'非常小心 - see other question
  • Demo @Turtlefight 所描述的事情
  • @NathanOliver 这将解决内存部分,很好。第二部分:由于需要对输入键进行类型转换,我担心 map.find(aStringView) 会很慢。在我的情况下,我正在搜索的键是 string_view。
  • 使用繁琐易出错

标签: c++ c++17


【解决方案1】:

在无序映射中使用 string_view 作为键的安全方法

一般来说没有,因为视图底层的存储可能随时更改,从而使您的地图不变量无效。

关联容器通常拥有一个const 键来避免这种情况。

在您的特定情况下,将std::unordered_set&lt;Val, ValKeyHash, ValKeyEqual&gt; 与合适的散列和相等函子一起使用会更有意义。


编辑,这些合适的函子很简单

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 透明/投影版本之前。

【讨论】:

  • 你应该描述合适的函子
  • 如果此答案将显示合适的函子或指出正确的学习方向,那么我可以将其标记为已接受。
【解决方案2】:

,你应该这样做

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 中的侵入式映射是一个带有投影的集合,而不是一个将值作为键的引用的映射。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-27
    • 2023-01-23
    • 2018-07-09
    • 1970-01-01
    • 2012-07-09
    • 2020-03-09
    • 2018-11-09
    • 2017-08-26
    相关资源
    最近更新 更多