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.
Hash
and Equality
need to have using is_transparent = std::true_type;
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<>>
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" });