c++c++11unordered-mapconst-cast

std::unordered_map data member with const key inside const method not compiling


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?


Solution

  • 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.