C++:将自定义哈希键值从 unordered_map 输出到 std::cout 时出错
C++: Error outputting a custom hash key value from unordered_map to std::cout
我正在尝试构建一个 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
来自具有自定义键类型的无序映射的哈希值的明确答案。任何反馈将不胜感激。
提前致谢!
编辑:
谢谢大家的意见。这是我第一次遇到非原始类型作为散列键,所以我对 key/value 对的存储方式有错误的想法(我假设散列值是键,而实际上它是实际的自定义类型)。
据我所知,没有用于从 std::unordered_map
.
公开散列值(与散列函数相反)的标准接口
如您所见,取消引用 std::unordered_map<Key,V>::iterator
会产生可转换为 std::unordered_map<Key,V>::value_type
的内容,而 std::unordered_map<Key,V>::value_type
又是表示 (key,value) 对的 std::pair<const Key,V>
,而不是 (散列键,值)对。
相应地,it.first
给你 std::vector<double>
而不是 std::size_t
。
unordered_map<K,V>
的 value_type
是 pair<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
。您的代码 breaks up completely on 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
后跟一个不合格的调用为 begin
和 end
.
启用 ADL
然后test
的定义就变成了
std::unordered_map<std::vector<double>, double, container_hasher> test;
我正在尝试构建一个 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
来自具有自定义键类型的无序映射的哈希值的明确答案。任何反馈将不胜感激。
提前致谢!
编辑:
谢谢大家的意见。这是我第一次遇到非原始类型作为散列键,所以我对 key/value 对的存储方式有错误的想法(我假设散列值是键,而实际上它是实际的自定义类型)。
据我所知,没有用于从 std::unordered_map
.
如您所见,取消引用 std::unordered_map<Key,V>::iterator
会产生可转换为 std::unordered_map<Key,V>::value_type
的内容,而 std::unordered_map<Key,V>::value_type
又是表示 (key,value) 对的 std::pair<const Key,V>
,而不是 (散列键,值)对。
相应地,it.first
给你 std::vector<double>
而不是 std::size_t
。
unordered_map<K,V>
的 value_type
是 pair<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
。您的代码 breaks up completely on 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
后跟一个不合格的调用为 begin
和 end
.
然后test
的定义就变成了
std::unordered_map<std::vector<double>, double, container_hasher> test;