Search code examples
c++c++11hashc++-chrono

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()?


Solution

  • 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());