Предрасчитанный ключ - вполне валидная стратегия, если расчёт хеша сильнее влияет на производительность, чем хранение дополнительных 8 байтов на элемент.
template <typename T>
class hashed {
T value_;
size_t hash_;
friend class std::hash<hashed<T>>;
public:
template <typename... Args>
constexpr hashed(Args&&... args) :
value_(std::forward<Args>(args)...),
hash_(std::hash<T>()(value_))
{}
// rule of zero
constexpr T& get() & noexcept { return value_; }
constexpr const T& get() const& noexcept { return value_; }
constexpr T&& get() && noexcept { return value_; }
constexpr const T&& get() const&& noexcept { return value_; }
};
namespace std {
template <typename T>
struct hash<hashed<T>> {
constexpr size_t operator()(const hashed<T>& x) const noexcept {
return x.hash_;
}
};
}
std::unordered_map<hashed<std::string>, int> cache;
auto it = cache.find("property1"); // хеш на этапе выполнения
// хеш на этапе компиляции, работает с constexpr std::string из C++20
constexpr auto h = hashed<std::string>("property1");
auto it2 = cache.find(h); // предрасчитанный хеш