Robinson
Robinson

Reputation: 10132

std::hash for std::chrono::duration

I'm trying to key a set of objects on a std::chrono::duration. This will not compile:

#include <unordered_map>
#include <chrono>

class Foo final
{
public:

    Foo() {}

    int y;
};


int main(void)
{
    auto map = std::unordered_map<std::chrono::duration<int, std::milli>, Foo>();

    map[std::chrono::duration<int, std::milli>(5)].y = 0;

    return 0;
}

/usr/include/c++/4.9/bits/hashtable_policy.h: In instantiation of 'struct std::__detail::__is_noexcept_hash >, std::hash > > >':

I'm guessing the problem here is there's no std::hash implementation for std::chrono::duration? If not, is there a way of doing this without resorting to keying on the eminently breakable count()?

Upvotes: 4

Views: 1711

Answers (1)

R2RT
R2RT

Reputation: 2146

Well, you could hide count under your own std::hash implementation http://en.cppreference.com/w/cpp/utility/hash:

#include <unordered_map>
#include <chrono>

class Foo final
{
public:

    Foo() {}

    int y;
};

using namespace std::chrono_literals;

// custom specialization of std::hash can be injected in namespace std
namespace std
{
    template<typename _rep, typename ratio>
    struct hash<std::chrono::duration<_rep, ratio>>
    {
        typedef std::chrono::duration<_rep, ratio> argument_type;
        typedef std::size_t result_type;
        result_type operator()(argument_type const& s) const
        {
            return std::hash<_rep>{}(s.count());
        }
    };
}


int main(void)
{
    auto map = std::unordered_map<std::chrono::duration<float, std::milli>, Foo>();

    map[std::chrono::duration<float, std::milli>(5)].y = 12;
    std::cout << map[5ms].y; // thanks to std::chrono_literals

    return 0;
}

If you are afraid of passing different ratios into your map you could also use static_cast into some minimal time precision, eg. std::micro:

return std::hash<_rep>{}( static_cast<std::chrono::duration<_rep, std::micro>>(s).count()); 

Upvotes: 3

Related Questions