在gcc 12.2中,下面的C++代码给了我一个错误。抱怨的要点是“散列函数必须可以用key类型的参数调用”:
#include <cstddef>
#include <unordered_map>
#include <functional>
struct OUTER {
class KEY {
public:
bool operator==(KEY const&) const;
struct Hash {
auto operator()(KEY const&) const {
// Give the hash for ten -- why not?
return std::hash<size_t>{}(10);
};
};
};
std::unordered_map<KEY, int, KEY::Hash> m;
bool test(KEY const& ident) { return m.end() != m.find(ident); }
};
错误消息为:
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/hashtable.h:35,
from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/unordered_map:46,
from <source>:2:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/hashtable_policy.h: In instantiation of 'std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash, _Unused, __cache_hash_code>::__hash_code std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash, _Unused, __cache_hash_code>::_M_hash_code(const _Key&) const [with _Key = OUTER::KEY; _Value = std::pair<const OUTER::KEY, int>; _ExtractKey = std::__detail::_Select1st; _Hash = OUTER::KEY::Hash; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; bool __cache_hash_code = true; __hash_code = long unsigned int]':
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/hashtable.h:1653:46: required from 'std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::iterator std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::find(const key_type&) [with _Key = OUTER::KEY; _Value = std::pair<const OUTER::KEY, int>; _Alloc = std::allocator<std::pair<const OUTER::KEY, int> >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<OUTER::KEY>; _Hash = OUTER::KEY::Hash; _RangeHash = std::__detail::_Mod_range_hashing; _Unused = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; iterator = std::__detail::_Insert_base<OUTER::KEY, std::pair<const OUTER::KEY, int>, std::allocator<std::pair<const OUTER::KEY, int> >, std::__detail::_Select1st, std::equal_to<OUTER::KEY>, OUTER::KEY::Hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::iterator; key_type = OUTER::KEY]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unordered_map.h:869:25: required from 'std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::iterator std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::find(const key_type&) [with _Key = OUTER::KEY; _Tp = int; _Hash = OUTER::KEY::Hash; _Pred = std::equal_to<OUTER::KEY>; _Alloc = std::allocator<std::pair<const OUTER::KEY, int> >; iterator = std::__detail::_Insert_base<OUTER::KEY, std::pair<const OUTER::KEY, int>, std::allocator<std::pair<const OUTER::KEY, int> >, std::__detail::_Select1st, std::equal_to<OUTER::KEY>, OUTER::KEY::Hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::iterator; key_type = OUTER::KEY]'
<source>:17:59: required from here
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/hashtable_policy.h:1268:23: error: static assertion failed: hash function must be invocable with an argument of key type
1268 | static_assert(__is_invocable<const _Hash&, const _Key&>{},
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/hashtable_policy.h:1268:23: note: 'std::__is_invocable<const OUTER::KEY::Hash&, const OUTER::KEY&>()' evaluates to false
Compiler returned: 1
既然OUTER::KEY::Hash::operator()
采用KEY const&
作为其参数类型,那么我应该是好的吧?
我希望上面的代码能够工作,因为我的哈希函数确实接受键类型作为参数。
事实上,我收到了一个错误!
2条答案
按热度按时间jmo0nnb31#
为
OUTER::KEY::Hash::operator()
声明一个显式返回类型似乎可以解决这个问题。std::hash
对象通常返回size_t
,因此这是这里返回类型的一个很好的候选。不过,我不知道错误的确切原因,因为如果删除
class OUTER
,非常相似的代码似乎可以编译。jum4pzuy2#
看起来这与 C++ DR 1890有关。
尽管C++标准中没有明确指出这段代码是不正确的,但g++, clang++, and MSVC all reject it。
您的示例与此类似,示例化
std::unordered_map
模板专门化涉及到检查键类型是否可以传递给哈希类型。也就是说,decltype(std::declval<OUTER::KEY::Hash const&>()(std::declval<OUTER::KEY const&>()))
是有效类型。我认为下一个例子说明了为什么这类事情与一个没有明确答案的更普遍的问题有关:
在成员函数体中,封闭类被认为是完整的。(它是
double
),而不是函数::T
,这只有在编译器在处理foo
的主体之前处理using T
时才会发生,但处理using U
只能在处理foo
的主体之后才能工作。如果foo
涉及到U
这个名字,事情就更乱了。