http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51558
Bug #: 51558
Summary: Declaration of unspecialized
std::hash<_Tp>::operator()(_Tp) turns compile-time
errors into link-time errors
Classification: Unclassified
Product: gcc
Version: 4.7.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libstdc++
AssignedTo: [email protected]
ReportedBy: [email protected]
Created attachment 26090
--> http://gcc.gnu.org/bugzilla/attachment.cgi?id=26090
Declare std::hash<T> instead of defining it.
libstdc++'s current definition of the unspecialized std::hash template gives
bad error messages when a user forgets to define a hash function for their
type. Specifically, providing a declaration but no definition of operator()
moves the error from compile to link time:
$ cat test.cc
#include <unordered_set>
struct MyStruct {
int i;
};
bool operator==(const MyStruct& lhs, const MyStruct& rhs) {
return lhs.i == rhs.i;
}
int main() {
std::unordered_set<MyStruct> s;
s.insert(MyStruct{3});
}
$ g++ -g -std=c++11 test.cc -o test
/tmp/cclzhwaU.o: In function `std::__detail::_Hash_code_base<MyStruct,
MyStruct, std::_Identity<MyStruct>, std::equal_to<MyStruct>,
std::hash<MyStruct>, std::__detail::_Mod_range_hashing,
std::__detail::_Default_ranged_hash, true>::_M_hash_code(MyStruct const&)
const':
.../include/c++/4.7.0/bits/hashtable_policy.h:702: undefined reference to
`std::hash<MyStruct>::operator()(MyStruct) const'
collect2: error: ld returned 1 exit status
With the attached patch, the error is much more informative, if not exactly
concise:
$ g++ -g -std=c++11 test.cc -o test
In file included from .../include/c++/4.7.0/bits/hashtable.h:35:0,
from .../include/c++/4.7.0/unordered_set:45,
from test.cc:1:
.../include/c++/4.7.0/bits/hashtable_policy.h: In instantiation of ‘struct
std::__detail::_Hash_code_base<MyStruct, MyStruct, std::_Identity<MyStruct>,
std::equal_to<MyStruct>, std::hash<MyStruct>,
std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, true>’:
.../include/c++/4.7.0/bits/hashtable.h:149:11: required from ‘class
std::_Hashtable<MyStruct, MyStruct, std::allocator<MyStruct>,
std::_Identity<MyStruct>, std::equal_to<MyStruct>, std::hash<MyStruct>,
std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash,
std::__detail::_Prime_rehash_policy, true, true, true>’
.../include/c++/4.7.0/bits/unordered_set.h:46:11: required from ‘class
std::__unordered_set<MyStruct, std::hash<MyStruct>, std::equal_to<MyStruct>,
std::allocator<MyStruct>, true>’
.../include/c++/4.7.0/bits/unordered_set.h:277:11: required from ‘class
std::unordered_set<MyStruct>’
test.cc:9:32: required from here
.../include/c++/4.7.0/bits/hashtable_policy.h:740:20: error:
‘std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2,
std::__detail::_Default_ranged_hash, true>::_M_h1’ has incomplete type
...
In particular, the "required from here" line points at the actual source
location that needs to be able to find the definition.
I believe the patch is allowed by C++11 since I can't find a specification of
the contents of the unspecialized template, and libc++ uses basically the same
technique:
http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/__functional_base?view=markup.
Another way to accomplish something similar would be to delete the operator()
declaration from the std::hash<T> definition. I believe that's not as good
because it produces error messages saying that std::hash lacks an operator()
rather than that the template has incomplete type. Better yet would be finding
a way to include "please specialize me" in the error message.