c++ - 在命名空间 std 之外专门化 std::hash 是否不安全?

标签 c++ templates namespaces template-specialization specialization

我知道我们只能向namespace std添加代码这是在那里定义的模板的模板特化,否则行为未定义。
我已经从 C++ 入门实现了这段代码:

//namespace std
//{
    template <>
    class std::hash<Sales_data>
//   class hash<Sales_data>
    {
    public:
        using result_type = std::size_t;
        using argument_type = Sales_data;
        result_type operator()(argument_type const& ) const;
    };

    typename hash<Sales_data>::result_type
    hash<Sales_data>::operator()(argument_type const& arg) const
    {
        return hash<std::string>()(arg.isbn_) ^
                hash<unsigned int>()(arg.nCopySold_) ^
                hash<double>()(arg.revenue_);
    }
//}
  • 代码工作正常,所以我将代码注入(inject)命名空间 std:template <> class std::hash<Sales_data>或者干脆我专攻 std::hash在命名空间之外?原始代码使用的是未注释的行。
  • 我不应该这样做还是这样做是错误的?
  • 最佳答案

    Is specializing std::hash outside namespace std unsafe?


    template<> class std::hash<Sales_data> { ... };在全局命名空间中将导致与
    namespace std {
    template<>
    class hash<Sales_data> { ... };
    }
    
    在全局命名空间中。

    Shouldn't I do this or it is erroneous to do so?


    这样做很好 - 但如果你像你在问题中那样做,那么旧版本的 g++(我认为版本 7 之前的版本,仍在使用中)会产生类似“warning: specialization of template in different namespace”的警告。结合-Werror这可能会导致完全正确的代码无法编译。我建议使用 namespace { ... }版本。

    关于哈希本身的注释:std::hash<any_type_smaller_or_of_equal_size_of_size_t>可能是标准库中非常差的散列(更改 1 位不会翻转大约一半的其他位),返回与输入相同的输出(位模式)。这很常见,因为它非常快,并且在用作标准容器中的 key 时可以满足所有需要。每个值都会得到一个唯一的哈希值,所以从这个角度来看它是完美的。将这些组合在一起时就会出现问题。碰撞将非常容易创建 - 您希望将碰撞降至最低以在您的 unordered 中进行查找。容器快。您的 XOR操作不会改变冲突很多的事实,所以我建议使用一个函数来组合散列。 boost有一个常用的( boost::hash_combine )。
    32 位看起来像这样 size_t s(不确定是否在不同的 boost 版本中有所不同):
    template <class T>
    inline void hash_combine(std::size_t& seed, const T& v)
    {
        std::hash<T> hasher;
        seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    }
    
    例子:
    namespace stolen { // from boost - use the real boost version to get proper 64 bit hashes
        template <class T>
        inline void hash_combine(std::size_t& seed, const T& v)
        {
            std::hash<T> hasher;
            seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
        }
    }
    
    namespace std {
        template <>
        class hash<Sales_data> {
        public:
            using result_type = std::size_t;
            using argument_type = Sales_data;
    
            result_type operator()(argument_type const& arg) const {
                result_type result = hash<std::string>{}(arg.isbn_);
                stolen::hash_combine(result, arg.nCopySold_);
                stolen::hash_combine(result, arg.revenue_);
                return result;
            }
        };
    }
    
    还有一个建议是添加一些版本的 hash_combine进入标准库: hash_combine() Again

    关于c++ - 在命名空间 std 之外专门化 std::hash 是否不安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65485237/

    相关文章:

    xml - 我可以用命名空间注册 xsd 然后验证 xml 忽略命名空间吗

    c# - 如何在不影响其他项目的情况下重构C#中的命名空间?

    c# - Nuget 包未找到和/或使用 System.Security.Cryptography

    c++ - 静态 const 成员的 const_cast

    c++ - 应该如何设计两个紧密相关的数据结构的所有权?

    c++ - 一个空队列需要多少字节?

    C++ 如果我编写一个函数模板并且不请求它的任何实例化,是否会从中生成任何模板函数?

    int 数组中的 C++ 分隔符

    html - 从范围中获取索引号

    javascript - 如何在 Genshin 中呈现 javascript 模板?