GCC and Clang don't compile std::hash<std::nullptr_t> in C++17

  • A+
Category:Languages

On https://en.cppreference.com/w/cpp/utility/hash it says that since C++17

Each standard library header that declares the template std::hash provides enabled specializations of std::hash for std::nullptr_t and all cv-unqualified arithmetic types (including any extended integer types), all enumeration types, and all pointer types.

So, a C++17 compliant compiler should compile this little program:

#include <functional> int main() {     std::hash<std::nullptr_t> h;     return h(nullptr); } 

However, GCC and Clang both are reporting an error saying that the default constructor of std::hash<std::nullptr_t> is (implicitly) deleted. See here and here to verify it yourself.

Visual Studio does compile it. Apparently it returns 0 672807365.

Q1: Are GCC and Clang simply still missing this C++17 feature, as admittedly this is not a high priority one? Or am I missing something?

Q2: Can I just specialize it myself and return 0 672807365 like Visual Studio? Wouldn't some other value, e.g. some prime, be better for combining it with other hashes?


Update

Due to my limited assembler knowledge I thought that Visual Studio is returning 0. In fact, it is returning 672807365 (the value in eax). So, my second question basically answers itself: I will not return 0 in my specialization to workaround this bug.

 


Is this program correct?

cppreference.com is right. From the latest C++ Standard draft:

[unord.hash]/2

Each specialization of hash is either enabled or disabled, as described below. [...] Each header that declares the template hash provides enabled specializations of hash for nullptr_­t and all cv-unqualified arithmetic, enumeration, and pointer types.

Since <functional> declares the hash template1, it must provide an enabled specialization for std::hash<std::nullptr_t>. Your example program should be accepted by any conforming C++17 implementation.


Why it is not though?

C++17 being still young, some subtle features might be missing still or buggy on recent compilers. Be reassured, your MCVE is accepted by gcc and clang in their development/experimental branches.

We couldn't find a development version of GCC accepting it though; this is why a bug report has been raised by Lightness Races in Orbit (see std::hash not implemented) and fixed by Jonathan Wakely (see revision267845) (and it returns zero).


How to fix your program while you're waiting for your implementation to get fixed?

Can I just specialize it myself and return 0 like Visual Studio?

You would be writing code that will exhibit Undefined Behavior2. Do it at your own risk. Document it well. For instance, put the following in a separate translation unit:

#include <functional> #include <type_traits> static_assert(     false == std::is_default_constructible_v<std::hash<std::nullptr_t>>,     "Explanation" ); 

This will warn your colleagues and ask them to manually remove your specialization of std::hash<std::nullptr_t> rather than get them a nasty compilation error.


1) See [functional.syn].

2) You are only allowed to specialize std class templates for program-defined types (which nullptr_­t isn't). You could also break the One Definition Rule.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: