Предрасчитанный ключ - вполне валидная стратегия, если расчёт хеша сильнее влияет на производительность, чем хранение дополнительных 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);  // предрасчитанный хеш