Home > database >  Safe way to use string_view as key in unordered map
Safe way to use string_view as key in unordered map

Time:10-23

My type Val contains std::string thekey.

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

I would like put my type in an unordered map, with thekey as key. For memory and conversion avoidance reasons I would like to have std::string_view as key type. Is it possible to have the key created to point to val.thekey, while using unique_ptr ?

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?

CodePudding user response:

Safe way to use string_view as key in unordered map

In general there isn't one, because the storage underlying the view might change at any time, invalidating your map invariants.

Associative containers generally own a const key precisely to avoid this.

In your specific case it makes much more sense to use std::unordered_set<Val, ValKeyHash, ValKeyEqual> with suitable hash and equality functors.

CodePudding user response:

In , you should do this

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;

now you can

theset.find("hello")

to find the element of the set whose key is "hello".


A map is fundamentally wrong here, because the features that a map has that the above set does not don't do the right things. Like mymap["hello"], which goes and creates a Val if it isn't found; we now have a dangling string view in the container.

An intrusive map in std is a set with a projection, not a map with a reference into the value as a key.

  • Related