在 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 版本之前是这样。
在c++20,你应该这样做
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
中的侵入式地图是带有投影的集合,而不是将值作为键的引用的地图。
我的类型 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 版本之前是这样。
在c++20,你应该这样做
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
中的侵入式地图是带有投影的集合,而不是将值作为键的引用的地图。