Section: 22.10.19 [unord.hash] Status: Resolved Submitter: Ville Voutilainen Opened: 2015-09-27 Last modified: 2017-02-02
Priority: 2
View all other issues in [unord.hash].
View all issues with Resolved status.
Discussion:
The rationale in issue 2148 says:
This proposed resolution doesn't specify anything else about the primary template, allowing implementations to do whatever they want for non-enums:
static_assert
nicely, explode horribly at compiletime or runtime, etc.
libc++ seems to implement it by defining the primary template and
static_assert
ing is_enum
inside it. However, that brings forth
a problem; there are reasonable SFINAE uses broken by it:
#include <type_traits> #include <functional> class S{}; // No hash specialization template<class T> auto f(int) -> decltype(std::hash<T>(), std::true_type()); template<class T> auto f(...) -> decltype(std::false_type()); static_assert(!decltype(f<S>(0))::value, "");
MSVC doesn't seem to accept that code either.
There is a way to implement LWG 2148 so thathash
for enums is supported
without breaking that sort of SFINAE uses:
Derive the main hash
template from a library-internal uglified-named
base template that takes a type and a bool
, pass as argument for the base
the result of is_enum
.
Partially specialize that base template so that the false-case has a suitable set of private special member function declarations so that it's not an aggregate nor usable in almost any expression.
[2015-10, Kona Saturday afternoon]
EricWF to come back with wording; move to Open
[2016-05-08, Eric Fiselier & Ville provide wording]
[2016-05-25, Tim Song comments]
I see two issues with this P/R:
"for which neither the library nor the user provides an explicit specialization" should probably be "for which neither the library nor the user provides an explicit or partial specialization".
Saying that the specialization "is not DefaultConstructible
nor MoveAssignable
" is not enough to
guarantee that common SFINAE uses will work. Both of those requirements have several parts, and it's not too hard
to fail only some of them. For instance, not meeting the assignment postcondition breaks MoveAssignable
,
but is usually not SFINAE-detectible. And for DefaultConstructible
, it's easy to write something in a way
that breaks T()
but not T{}
(due to aggregate initialization in the latter case).
[2016-06-14, Daniel comments]
The problematic part of the P/R is that it describes constraints that would be suitable if they were constraints
for user-code, but they are not suitable as requirements imposed on implementations to provide certain guarantees
for clients of the Library. The guarantees should be written in terms of testable compile-time expressions, e.g. based on
negative results of is_default_constructible<hash<X>>::value
,
std::is_copy_constructible<hash<X>>::value
, and possibly also
std::is_destructible<hash<X>>::value
. How an implementation realizes these negative
results shouldn't be specified, though, but the expressions need to be well-formed and well-defined.
[2016-08-03, Ville provides revised wording as response to Daniel's previous comment]
Previous resolution [SUPERSEDED]:This wording is relative to N4582.
Insert a new paragraph after 22.10.19 [unord.hash]/2
-2- The template specializations shall meet the requirements of class template
-?- For any type that is not of integral or enumeration type, or for which neither the library nor the user provides an explicit specialization of the class templatehash
(20.12.14).hash
, the specialization ofhash
does not meet any of theHash
requirements, and is notDefaultConstructible
norMoveAssignable
. [Note: this means that the specialization ofhash
exists, but any attempts to use it as a Hash will be ill-formed. — end note]
[2016-08 - Chicago]
Thurs AM: Moved to Tentatively Ready
Previous resolution [SUPERSEDED]:This wording is relative to N4606.
Insert a new paragraph after 22.10.19 [unord.hash]/2
[Drafting note: I see no reason to specify whether
H<T>
is destructible. There's no practical use case for which that would need to be covered. libstdc++ makes it so thatH<T>
is destructible.]-2- The template specializations shall meet the requirements of class template
-?- For any typehash
(20.12.14).T
that is not of integral or enumeration type, or for which neither the library nor the user provides an explicit or partial specialization of the class template hash, the specialization ofhash<T>
has the following properties:
is_default_constructible_v<hash<T>>
isfalse
is_copy_constructible_v<hash<T>>
isfalse
is_move_constructible_v<hash<T>>
isfalse
is_copy_assignable_v<hash<T>>
isfalse
is_move_assignable_v<hash<T>>
isfalse
is_callable_v<hash<T>, T&>
isfalse
is_callable_v<hash<T>, const T&>
isfalse
[Note: this means that the specialization of
hash
exists, but any attempts to use it as aHash
will be ill-formed. — end note]
[2016-08-09 Daniel reopens]
As pointed out by Eric, the usage of is_callable
is incorrect.
Eric provides new wording.
[2016-09-09 Issues Resolution Telecon]
Move to Tentatively Ready
[2016-11-12, Issaquah]
Resolved by P0513R0
Proposed resolution:
This wording is relative to N4606.
Insert a new paragraph after 22.10.19 [unord.hash]/2
[Drafting note: I see no reason to specify whether
H<T>
is destructible. There's no practical use case for which that would need to be covered. libstdc++ makes it so thatH<T>
is destructible.]
-2- The template specializations shall meet the requirements of class template
-?- For any typehash
(20.12.14).T
that is not of integral or enumeration type, or for which neither the library nor the user provides an explicit or partial specialization of the class template hash, the specialization ofhash<T>
has the following properties:
is_default_constructible_v<hash<T>>
isfalse
is_copy_constructible_v<hash<T>>
isfalse
is_move_constructible_v<hash<T>>
isfalse
is_copy_assignable_v<hash<T>>
isfalse
is_move_assignable_v<hash<T>>
isfalse
hash<T>
is not a function object type (22.10 [function.objects])[Note: this means that the specialization of
hash
exists, but any attempts to use it as aHash
will be ill-formed. — end note]