C++:将自定义哈希键值从 unordered_map 输出到 std::cout 时出错

C++: Error outputting a custom hash key value from unordered_map to std::cout

本文关键字:输出 std 出错 map cout unordered 自定义 哈希 键值 C++      更新时间:2023-10-16

我正在尝试构建一个以自定义类型作为键的std::unordered_map。自定义类型是一个简单的std::vector<double>。这个想法是,它将作为网格上 2D 点的方便容器。除了输出散列密钥外,一切都正常工作。以下是我整理的一个示例来说明这个想法:

#include <iostream>
#include <vector>
#include <unordered_map>
#include <boost/functional/hash.hpp>
#include <chrono>
namespace std
{
    template<typename Container>
    struct hash {
            std::size_t operator()(Container const& v) const
            {
                return boost::hash_range(v.begin(), v.end());
            }
    };
}
int main()
{
    std::unordered_map<std::vector<double>, double> test;
    unsigned long t = (unsigned long) std::chrono::system_clock::now().time_since_epoch().count();
    std::srand(t);
    for (uint i = 0; i < 100 ; ++i)
    {
        double d1 = i/200.0;
        double d2 = i/200.0;
        std::vector<double> v({d1, d2});
        test[v] = d1;
    }
    std::cout << "Size:" << test.size() << std::endl;
    for (const auto& it : test )
    {
        std::cout << it.first << ":" << it.second << std::endl;
    }
    return 0;
}

哈希专用化模板由另一个 SO 线程提供。问题是,当我尝试编译上述内容时,g++ 会吐出以下错误:

cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
   std::cout << it.first << ":" << it.second << std::endl;
                   ^

很明显,它偶然发现了it.first.如果我删除it.first,代码可以正确编译和运行。我知道输出不会是双精度的向量。我确实环顾了 SO 一段时间,但我找不到有关如何从具有自定义键类型的无序映射中std::cout哈希值的明确答案。任何反馈将不胜感激。

提前谢谢你!

编辑:

谢谢大家的投入。这是我第一次遇到非基元类型作为散列键,所以我对键/值对的存储方式有错误的想法(我假设散列值是键,而实际上它是实际的自定义类型(。

unordered_map<K,V>value_typepair<const K, V> 。这就是您使用范围进行迭代时得到的结果。vector 秒没有operator<<过载,导致您看到的错误。


namespace std
{
    template<typename Container>
    struct hash {
            std::size_t operator()(Container const& v) const
            {
                return boost::hash_range(v.begin(), v.end());
            }
    };
}

这不是std::hash的专业化.这是对主模板的重新定义,在您的情况下,主模板仅由纯粹偶然编译。(实现必须保持主std::hash模板未定义,并且必须在std命名空间而不是内联命名空间中实际声明hash。例如,您的代码在libc ++上完全分解。

专业化看起来像

namespace std
{
    // full specialization
    template<>
    struct hash<Foo> {
    //         ^^^^^
            std::size_t operator()(Foo const& v) const
            {
                // ...
            }
    };
    // partial specialization
    template<typename T>
    struct hash<Bar<T>>{
    //         ^^^^^^^^
            std::size_t operator()(Bar<T> const& v) const
            {
                // ...
            }
    };
}

请注意 hash 后面的显式模板参数列表。这表明这是一种专业化。

无论如何,将

std::hash专用化std::vector<double>都是非法的,因为它不依赖于用户定义的类型。编写自己的哈希器很容易:

struct container_hasher {
    template<typename Container>
    std::size_t operator()(Container const& v) const
    {
        using std::begin; 
        using std::end;
        return boost::hash_range(begin(v), end(v));
    }
};

请注意,我模板化了operator()而不是类型本身 - 这使得编写哈希器类型更容易。using后跟非限定调用使 ADL 能够beginend

然后test的定义就变成了

std::unordered_map<std::vector<double>, double, container_hasher> test;

我所知,没有标准的接口可以从std::unordered_map公开哈希值(与哈希函数相反(。

如您所见,取消引用std::unordered_map<Key,V>::iterator会产生可转换为std::unordered_map<Key,V>::value_type的东西,而又是表示(键,值(对的std::pair<const Key,V>,而不是(散列键,值(对。

相应地,it.first给你一个std::vector<double>而不是一个std::size_t