简体   繁体   English

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

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

I am trying to build an std::unordered_map with a custom type as a key. 我正在尝试使用自定义类型作为键来构建std::unordered_map The custom type is a simple std::vector<double> . 自定义类型是简单的std::vector<double> The idea is that it will function as a convenient container for 2D points on a grid. 这个想法是,它将充当网格上2D点的便捷容器。 Everything is working correctly except for outputting the hashed key. 除了输出散列键之外,其他所有东西都正常工作。 Here is a sample I put together to illustrate the idea: 我整理了一个示例来说明这个想法:

#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;
}

The hash specialisation template is courtesy of another SO thread. 哈希专门化模板由另一个SO线程提供。 The trouble is that g++ spits out the following error when I try to compile the above: 问题是当我尝试编译以上代码时,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 is obvious that it stumbles on it.first . 很明显,它it.first The code compiles and runs correctly if I remove it.first . 如果我删除it.first ,代码将编译并正确运行。 I understand that the output will not be a vector of doubles. 我知道输出不会是双精度的向量。 I did look around SO for quite a while, but I couldn't find a definitive answer on how to std::cout the hash value from an unordered map with a custom key type. 我确实四处逛逛,但是我找不到关于如何从具有自定义键类型的无序映射中std::cout哈希值的确切答案。 Any feedback will be highly appreciated. 任何反馈将不胜感激。

Thank you in advance! 先感谢您!

Edit: 编辑:

Thank you everyone for your input. 谢谢大家的投入。 This was my first encounter with non-primitive types as hashed keys, so I had the wrong idea about how the key/value pairs were stored (I assumed that the hashed value is the key, whereas in fact it is the actual custom type). 这是我第一次遇到非原始类型作为散列键的情况,因此我对键/值对的存储方式有错误的认识(我假设散列值是键,而实际上是实际的自定义类型) 。

The value_type of an unordered_map<K,V> is pair<const K, V> . unordered_map<K,V>value_typepair<const K, V> That's what you get when you iterate over it with a range-for. 那就是当您对范围进行迭代时所得到的。 There's no operator<< overload for vector s, causing the error you see. vector s没有operator<<重载,从而导致您看到的错误。


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

This isn't a specialization of std::hash . 这不是std::hash的专业化。 It's a redefinition of the primary template, which in your case only compiled by pure happenstance. 它是对主模板的重新定义,在您的情况下,该模板仅由纯偶然性编译。 (The implementation will have to leave the primary std::hash template undefined, and will have to actually declare hash in the std namespace and not an inlined namespace. Your code breaks up completely on libc++ , for example.) (该实现将必须保留未定义的主要std::hash模板,并且必须在std名称空间而不是内联名称空间中实际声明hash 。例如,您的代码在libc ++上完全崩溃 。)

A specialization would look like 一个专业化看起来像

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
            {
                // ...
            }
    };
}

Note the explicit template argument list following hash . 注意hash的显式模板参数列表。 That's the indication that this is a specialization. 这表明这是专业化。

It is illegal to specialize std::hash for std::vector<double> anyway, because it doesn't depend on a user-defined type. 无论如何,将std::hash专门用于std::vector<double>是非法的,因为它不依赖于用户定义的类型。 Writing your own hasher is easy: 编写自己的哈希器很容易:

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));
    }
};

Note that I templated the operator() instead of the type itself - this makes writing the hasher type easier. 请注意,我对operator()而不是类型本身进行了模板化-这使编写哈希器类型更加容易。 The using followed by an unqualified call enables ADL for begin and end . using后跟不合格的调用使ADL可以beginend

And then the definition of test becomes 然后test的定义变成

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

As far as I am aware, there is no standard interface for exposing the hash value (as opposed to the hashing function) from std::unordered_map . 据我所知,没有标准接口可用于从std::unordered_map公开哈希值(与哈希函数相对)。

As you have seen, dereferencing a std::unordered_map<Key,V>::iterator yields something convertible to std::unordered_map<Key,V>::value_type , which in turn is a std::pair<const Key,V> representing a (key,value) pair, rather than a (hashed key,value) pair. 如您所见,取消引用std::unordered_map<Key,V>::iterator产生可转换为std::unordered_map<Key,V>::value_type ,而后者又是std::pair<const Key,V>代表(键,值)对,而不是(散列的键,值)对。

Correspondingly, it.first is giving you a std::vector<double> rather than a std::size_t . 相应地, it.first首先给您一个std::vector<double>而不是std::size_t

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM