GCC и Clang не компилируют std :: hash <std :: nullptr_t> в С++ 17

На https://en.cppreference.com/w/cpp/utility/hash говорится, что начиная с С++ 17

Каждый стандартный заголовок библиотеки, который объявляет шаблон std :: hash, обеспечивает включенные специализации std :: hash для std :: nullptr_t и всех арифметических типов без cv (включая любые расширенные целочисленные типы), всех типов перечисления и всех типов указателей.

Итак, компилятор, совместимый с С++ 17, должен скомпилировать эту маленькую программу:

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

Однако GCC и Clang сообщают об ошибке, говоря, что конструктор по умолчанию std::hash<std::nullptr_t> (неявно) удален. Смотрите здесь и здесь, чтобы убедиться в этом сами.

Visual Studio его компилирует. По-видимому, он возвращает 0 672807365.

Q1: GCC и Clang просто по-прежнему не имеют этой функции С++ 17, поскольку, по общему признанию, она не является приоритетной? Или я что-то упустил?

Q2: Могу ли я просто специализировать его самостоятельно и вернуть 0 672807365 как Visual Studio? Разве не лучше использовать какое-то другое значение, например, простое число, для объединения его с другими хэшами?


Обновить

Из-за моего ограниченного знания ассемблера я думал, что Visual Studio возвращает 0. Фактически он возвращает 672807365 (значение в eax). Итак, мой второй вопрос в основном отвечает сам по себе: я не верну 0 в моей специализации, чтобы обойти эту ошибку.

Ответ 1

Эта программа правильная?

cppreference.com прав. Из последней версии стандарта C++:

[unord.hash]/2

Каждая специализация хэша либо включена, либо отключена, как описано ниже. [...] Каждый заголовок, который объявляет хэш шаблона, предоставляет включенные специализации hash для nullptr_t и всех cv-неквалифицированных арифметических, перечислимых и указательных типов.

Так как <functional> объявляет hash шаблон 1 он должен предоставлять включенную специализацию для std::hash<std::nullptr_t>. Ваша примерная программа должна быть принята любой соответствующей C++ 17 реализацией.


Почему это не так, хотя?

C++ 17, будучи еще молодым, некоторые тонкие функции могут отсутствовать или содержать ошибки в последних компиляторах. Будьте уверены, ваш MCVE принят gcc и clang в их ветки разработки /эксперимента.

Мы не смогли найти версию GCC, которая бы его принимала; именно поэтому отчет об ошибках был создан Расами Легкости на орбите (см. std :: hash не реализован) и исправлен Джонатаном Уэйкли (см. revision267845) (и он возвращает ноль).


Как исправить вашу программу, пока вы ожидаете исправления вашей реализации?

Могу ли я просто специализировать его и вернуть 0, как Visual Studio?

Вы будете писать код, который будет демонстрировать неопределенное поведение 2. Делайте это на свой страх и риск. Документируйте это хорошо. Например, поместите следующее в отдельную единицу перевода:

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

Это предупредит ваших коллег и попросит их вручную удалить вашу специализацию std::hash<std::nullptr_t> вместо того, чтобы получить им неприятную ошибку компиляции.


1) См. [functional.syn].

2) Вам разрешено специализировать шаблоны классов std для программно-определяемых типов (что не является nullptr_t). Вы также можете нарушить правило единого определения.