I am trying to make an std::unordered_set whose key is of type std::tuple.
The below code compiled by MSVC gives me:
Error C3848 expression having type 'const _Hasher' would lose some const-volatile qualifiers
in order to call 'size_t TupleHash::operator ()<int,int,int>(const std::tuple<int,int,int> &)'
struct TupleHash
{
// Uncomment the const keyword below resolves the error
template<typename T1, typename T2, typename T3>
std::size_t operator()(const std::tuple<T1, T2,T3> &t) // const
{
return std::hash<T1>{}(std::get<0>(t)) ^ (std::hash<T2>{}(std::get<1>(t)) << 1) ^ ( ( std::hash<T2>{}(std::get<2>(t)) << 2 ) );
}
};
int main() {
std::unordered_set<std::tuple<int, int, int>, TupleHash> test;
test.insert(std::make_tuple(1, 2, 3));
test.insert(std::make_tuple(2, 3, 4));
test.insert(std::make_tuple(2, 3, 1));
test.insert(std::make_tuple(1, 2, 3));
for (auto& [field1, field2, field3] : test) {
printf("%d, %d, %d\n", field1, field2, field3);
}
}
Apparently, it is complaining that TupleHash::operator() is not declared as const. To fix it, I simply added a const keyword at the function signature.
Why does the regulation exist? I can understand that the argument of the hash function, i.e., std::tuple<T1, T2, T3> &t, needs to be constant, as hash container asks its element to be immutable, but why does the member function of the functor itself needs to be const too?
I just tested and confirmed GCC 12.3 behaves the same.
TupleHashin a more generic way (like this)const, it tells me that method does not modify the object (even if the method lies, and does modify the object). The absence ofconsttells me the method does mutate the object (even if the method doesn't). It's a matter of expectations and responsibilities. Since "C++: all the defaults are wrong", in my opinion all methods ought to be implicitlyconstby default, and otherwise need to be explicitly markedmutable. But that ship has sailed.unordered_map. So it will be const when used by one of the map's const methods, e.g.unordered_map::find(const Key&) const. And you can't call a mutable method on a const object. It would also violate the standard data race guarantees if the library allowed modifying potentially thread-shared members through a constthispointer.