Here is a small piece of code:
class myClass {
public:
void myMethod() const {
for (const auto& entity : myList) {
auto iter = myMap.find(&entity);
}
}
private:
std::list<int> myList;
std::unordered_map<int*,int> myMap;
};
The method myMethod()
is marked const since it should not change any data member.
The code does not compile, with a:
note: candidate function not viable: 1st argument ('const int *') would lose const qualifier
note: candidate function not viable: 'this' argument has type 'const std::unordered_map<int *, int>', but method is not marked const
note: candidate template ignored: requirement '__is_transparent_v<std::hash<int *>, const int *>' was not satisfied [with _K2 = const int *]
note: candidate template ignored: requirement '__is_transparent_v<std::hash<int *>, const int *>' was not satisfied [with _K2 = const int *]
Changing the std::unordered_map
with a std::map
does not compile either. Only a const_cast
on &entity
as auto iter = myMap.find(const_cast<int*>(&entity));
makes it compile.
Why do I need use a const_cast
?
In your code snippet, the std::unordered_map
attribute has int*
as its key_type
. This means that both the hasher and the key_equal types have been defatilted to, respectively, std::hash<int*>
and std::equal<int*>
.
However, the pointer that is passed to the find()
method in MyMethod()
is of const int*
type. This is because entity
is generated as a constant reference in the for-range loop. As a consequence, it is necessary to convert the pointer-to-const-T (const int*
) to pointer-to-T (int*
) to make it conformant with the function signature.
The alternative is to use custom hasher
and key_equal
types, which suppot heterogeneous lookup for both pointer-to-T and pointer-to-const-T objects. For instance, in C++14 has been added the specialization std::equal_to<void>
, which would work in that situation.