Title
LWG 2148 (hash support for enum types) seems under-specified
Status
resolved
Section
[unord.hash]
Submitter
Ville Voutilainen

Created on 2015-09-27.00:00:00 last changed 89 months ago

Messages

Date: 2016-11-21.17:34:16

Proposed resolution:

This wording is relative to N4606.

  1. Insert a new paragraph after [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 that H<T> is destructible.]

    -2- The template specializations shall meet the requirements of class template hash (20.12.14).

    -?- For any type 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 of hash<T> has the following properties:

    • is_default_constructible_v<hash<T>> is false
    • is_copy_constructible_v<hash<T>> is false
    • is_move_constructible_v<hash<T>> is false
    • is_copy_assignable_v<hash<T>> is false
    • is_move_assignable_v<hash<T>> is false
    • hash<T> is not a function object type ([function.objects])

    [Note: this means that the specialization of hash exists, but any attempts to use it as a Hash will be ill-formed. — end note]

Date: 2016-11-15.00:00:00

[ 2016-11-12, Issaquah ]

Resolved by P0513R0

Date: 2016-09-09.00:00:00

[ 2016-09-09 Issues Resolution Telecon ]

Move to Tentatively Ready

Date: 2016-08-09.00:00:00

[ 2016-08-09 Daniel reopens ]

As pointed out by Eric, the usage of is_callable is incorrect. Eric provides new wording.

Date: 2016-08-09.20:35:14

[ 2016-08 - Chicago ]

Thurs AM: Moved to Tentatively Ready

Previous resolution [SUPERSEDED]:

This wording is relative to N4606.

  1. Insert a new paragraph after [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 that H<T> is destructible.]

    -2- The template specializations shall meet the requirements of class template hash (20.12.14).

    -?- For any type 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 of hash<T> has the following properties:

    • is_default_constructible_v<hash<T>> is false
    • is_copy_constructible_v<hash<T>> is false
    • is_move_constructible_v<hash<T>> is false
    • is_copy_assignable_v<hash<T>> is false
    • is_move_assignable_v<hash<T>> is false
    • is_callable_v<hash<T>, T&> is false
    • is_callable_v<hash<T>, const T&> is false

    [Note: this means that the specialization of hash exists, but any attempts to use it as a Hash will be ill-formed. — end note]

Date: 2016-06-15.00:00:00

[ 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.

  1. Insert a new paragraph after [unord.hash]/2

    -2- The template specializations shall meet the requirements of class template hash (20.12.14).

    -?- 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 template hash, the specialization of hash does not meet any of the Hash requirements, and is not DefaultConstructible nor MoveAssignable. [Note: this means that the specialization of hash exists, but any attempts to use it as a Hash will be ill-formed. — end note]

Date: 2016-05-15.00:00:00

[ 2016-05-25, Tim Song comments ]

I see two issues with this P/R:

  1. "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".

  2. 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).

Date: 2016-05-15.00:00:00

[ 2016-05-08, Eric Fiselier & Ville provide wording ]

Date: 2015-11-04.16:49:21

[ 2015-10, Kona Saturday afternoon ]

EricWF to come back with wording; move to Open

Date: 2015-09-27.00:00:00

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_asserting 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 that hash for enums is supported without breaking that sort of SFINAE uses:

  1. 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.

  2. 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.

History
Date User Action Args
2016-11-21 17:34:16adminsetmessages: + msg8679
2016-11-21 17:34:16adminsetstatus: wp -> resolved
2016-11-14 03:59:28adminsetstatus: pending -> wp
2016-11-14 03:55:22adminsetstatus: ready -> pending
2016-09-12 04:46:33adminsetmessages: + msg8515
2016-09-12 04:46:33adminsetstatus: open -> ready
2016-08-09 20:35:14adminsetmessages: + msg8464
2016-08-09 20:35:14adminsetstatus: ready -> open
2016-08-05 03:33:05adminsetmessages: + msg8413
2016-08-05 03:33:05adminsetstatus: open -> ready
2016-06-14 21:01:34adminsetmessages: + msg8176
2016-06-14 21:01:34adminsetmessages: + msg8175
2016-05-11 20:17:52adminsetmessages: + msg8119
2016-05-11 20:17:52adminsetmessages: + msg8118
2015-11-04 16:49:21adminsetmessages: + msg7616
2015-11-04 16:49:21adminsetstatus: new -> open
2015-09-27 00:00:00admincreate