Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use a lightweight argument in unordered_set::find() method?

Must I fully build a Storage object for finding it, instead of using std::string because of having operator== for it?

#include <cstddef>
#include <functional>
#include <string>
#include <unordered_set>

struct Storage
{
  struct Hash
  {
    std::size_t operator()(const Storage& storage) const noexcept
    {
      return std::hash<std::string>{}(storage.key);
    }
  };

  std::string key;
  // One more fat fields...
};

bool
operator==(const Storage& lhs, const Storage& rhs) noexcept
{
  return lhs.key == rhs.key;
}

bool
operator==(const Storage& lhs, const std::string& rhs) noexcept
{
  return lhs.key == rhs;
}

int
main()
{
  auto uset = std::unordered_set<Storage, Storage::Hash, std::equal_to<>>
  {
    { .key="42" }
  };

  // Variant #1 - works
  auto it = uset.find({ .key="42" /* and init other fields */ });

  // Variant #2 - doesn't compiles :(
  auto it = uset.find(std::string("42"));
}

I tried operator std::string() for struct Storage, and I tried different variants of Storage::operator==() like this:

bool operator==(const Storage& lhs, const std::string& rhs)

bool operator==(const std::string& lhs, const Storage& rhs)

But it doesn't work.

like image 990
mistermad Avatar asked Oct 24 '25 14:10

mistermad


1 Answers

  1. your Hash and Equality need to have using is_transparent = std::true_type;
  2. your Hash needs to be able to hash both the Storage type and your find argument type.
struct Storage
{
  struct Hash
  {
    std::size_t operator()(const Storage& storage) const noexcept
    {
      return std::hash<std::string>{}(storage.key);
    }

    std::size_t operator()(std::string_view key) const noexcept
    {
        return std::hash<std::string_view>{}(key);
    }
    using is_transparent = std::true_type;
  };

  std::string key;
  // One more fat fields...
};

auto uset = std::unordered_set<Storage, Storage::Hash, std::equal_to<>>

online demo

you should replace std::string with std::string_view in both the hash and Equality to avoid the unnecessary copy, it is guaranteed to have the same hash as std::string, see the comment.

PS: don't forget inline on your free functions to avoid multiple definitions.


also note that this is now ambigious on MSVC

auto it = uset.find({ .key="42" });

as now find can take either std::string_view or Storage, you need to explicitly type it

auto it = uset.find(Storage{ .key="42" });
like image 148
Ahmed AEK Avatar answered Oct 27 '25 04:10

Ahmed AEK



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!