On Fri, 21 Nov 2025 at 10:23, Tomasz Kamiński <[email protected]> wrote:
>
> This patch implements P2592R3 Hashing support for std::chrono value classes.
>
> To avoid the know issues with current hashing of integer types (see PR104945),
> we use chrono::__int_hash function that hash the bytes of representation,
> instead of hash<T>, as the later simply cast to value. Currently _Hash_impl
> it used, but we should consider replacing it (see PR55815) before C++26 ABI
> is made stable. The function is declared inside <chrono> header and chrono
> namespace, to make sure that only chrono components would be affected by
> such change. Finally, chrono::__int_hash is made variadic, to support
> combining hashes of multiple integers.
>
> To reduce the number of calls to hasher (defined out of line), the calendar
> types are packed into single unsigned integer value. This is done by
> chrono::__hash helper, that calls:
> * chrono::__as_int to cast the value of single component, to unsigned integer
>   with size matching the one used by internal representation: unsigned short
>   for year/weekday_indexed, and unsigned char in all other cases,
> * chrono::__pack_ints to pack integers (if more than one) into single integer
>   by performing bit shift operations,
> * chrono::__int_hash to hash the value produced by above.
>
> Hashing of duration, time_point, and zoned_time only hashes the value and
> ignores any difference in the period, i.e. hashes of nanoseconds(2) and
> seconds(2) are the same. This does not affect the usages inside unordered
> containers, as the arguments are converted to key type first. To address
> that period::num and period::denum could be included in the hash, however
> such approach will not make hashes of equal durations (2000ms, 2s), so
> they would remain unusable for precomputed hashes. In consequence,
> including period in hash, would only increase runtime cost, withou any
> clear benefits.
>
> Futhermore,  chrono::__int_hash is used when the duration representation
> is integral type, and for other types (floating point due special handling
> of +/-0.0 and user defined types) we delegate to hash specialization.
> This is automatically picked up by time_point, that delegates to hasher
> of duration. Similarly for leap_second that is specified to use integer
> durations, we simply hash representations of date() and value(). Finally
> zoned_time in addition to handling integer durations as described above,
> we also use __int_hash for const time_zone* (if used), as hash<T*> have
> similar problems as hash specialization for integers. This is limited
> only to _TimeZonePtr being const time_zone* (default), as user can define
> hash specializations for raw pointers to they zones.
>
> As accessing the representation for duration requires calling count()
> method that returns a copy of representation by value, the noexcept
> specification of the hasher needs to take into consideration copy
> constructor of duration. Similar reasoning applies for time_since_epoch
> for time_points, and get_sys_time, get_time_zone for zoned_time.
> For all this cases we use internal __is_nothrow_copy_hashable concept.
>
> Finally support for zoned_time is provided only for CXX11 string ABI,
> __cpp_lib_chrono feature test macro cannot be bumped if COW string are used.
> To indicate presence of hasher for remaining types this patch also bumps
> the internal __glibcxx_chrono_cxx20 macro, and uses it as guard to new
> features.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/version.def (chrono, chrono_cxx20): Bump values.
>         * include/bits/version.h: Regenerate.
>         * include/std/chrono (__is_nothrow_copy_hashable)
>         (chrono::__pack_ints, chrono::__as_int, chrono::__int_hash)
>         (chrono::__hash): Define.
>         (std::hash): Define partial specialization for duration, time_point,
>         and zoned_time, and full specializations for calendar types and
>         leap_second.
>         (std::__is_fast_hash): Define partial specializations for duration,
>         time_point, zoned_time.
>         * testsuite/std/time/hash.cc: New test.
>
> Co-authored-by: Giuseppe D'Angelo <[email protected]>
> Signed-off-by: Tomasz Kamiński <[email protected]>
> Signed-off-by: Giuseppe D'Angelo <[email protected]>
> ---
> v5:
>  - add paragraph in commit description explaning, why period is not
>    included in hash
>  - remove code required for handling cv-qualified rep, we can add
>    them if necessary,

The C++26 draft is quite explicit:

"The specialization hash<chrono::duration<Rep, Period>> is enabled
(22.10.19) if and only if hash<Rep> is enabled."

So hash<duration<R, P>> should not use hash<remove_cv_t<R>>, and
therefore hash<duration<const int>> should be disabled.

Reply via email to