Проблема с кодом в вашем вопросе не в том, что это переопределение, а в том, что аргументы шаблона по умолчанию недопустимы в частичной специализации, и что все параметры шаблона в частичной специализации должны быть выводимыми.
std::hash
не предоставляет второй параметр шаблона в первичном шаблоне, который можно использовать для SFINAE, но на основе этого ответа вы можете сделать что-то подобное в качестве обходного пути:
#include <memory>
#include <utility>
class Base {};
class Derived : public Base {};
template <typename First, typename... Others>
using first = First;
namespace std {
template <typename T>
struct hash<first<std::shared_ptr<T>,
std::enable_if_t<std::is_base_of_v<Base, T>>>> {
size_t operator()(const std::shared_ptr<T>& d) const { return 616; }
};
} // namespace std
что, я бы предположил, в принципе нормально, потому что объявление зависит от определяемого пользователем типа Base
.
В стандарте существует нерешенная проблема относительно того, следует ли считать эту специализацию переопределением стандарта. специализация для std::shared_ptr
. (G CC считает, что это не так, Clang думает, что это так.)
Но что более важно, тогда у вас все еще есть проблема, что эта частичная специализация не более специализирована, чем та, которую стандарт предусматривает std::shared_ptr
. Поэтому любое фактическое использование может привести к ошибке неоднозначности, и я не думаю, что есть какой-либо способ сделать специализацию более специализированной.
Таким образом, я думаю, что ваше единственное решение - это определить явную специализацию для std::hash
для каждый производный тип, может быть, с помощью макроса. Или, в качестве альтернативы (и, возможно, более уместно), вы должны написать свой собственный хеш-функтор и указать его в качестве альтернативы std::hash
там, где это необходимо.