0

I have a std::unordered_set<int *> (in my real code I use pointers to a class, but int * works for this example too) and want to check whether a given pointer is stored in that set. Since the function contains will only be available for C++20, I'm using the function size_type count( const Key& key ) const.

If I'm now searching for a const pointer instead of a pointer, the compilation fails with the message error: invalid conversion from ‘const int*’ to ‘std::unordered_set<int*>::key_type {aka int*}’ [-fpermissive]

Example code:

#include <unordered_set>

int main() {
    std::unordered_set<int *> set {};

    int foo = 42;
    set.insert(&foo);

    int *pointer = &foo;
    set.count(pointer); // works fine

    const int *const_pointer = pointer;
    set.count(const_pointer); // doesn't work
    set.count(const_cast<int *>(const_pointer)); // works fine

    return 0;
}

I'm using g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 with C++11.

Is there a way to avoid that ugly const_cast<>? That error seems quite unimportant to me...

Asteroids With Wings
  • 16,164
  • 2
  • 17
  • 33
schetefan24
  • 106
  • 1
  • 7
  • Do you know the difference between `const int *` and `int * const`? In your example `const_pointer` is not a constant pointer, but a pointer to a constant. – Brian Apr 13 '20 at 17:11
  • 1
    As the pointer won't be used for anything other than comparison I'd say that `const_cast` is valid in this case and not "ugly". Maybe you can create your own `contains` for your specific set that takes a const pointer and does the cast inside? – simon Apr 13 '20 at 17:16
  • @Brian yes, I know the difference. Probably the wording was a bit bad in this question... adding a `const` behind that `*` doesn't change the behavior (it is added implicitly in the function call anyway) – schetefan24 Apr 13 '20 at 17:24
  • @simon A contains function wouldn't really make sense in my case because I want to use that in a function called `isConnected(const Node *const otherNode) { return ...;}` and is in that sense already the wrapper itself :D considering the const_cast as valid is totally fine, although I want to avoid it (I'm quite new to C++, only about 2 weeks, used mainly Java before) – schetefan24 Apr 13 '20 at 17:26
  • Can you change the set to `std::unordered_set set {};`? – Eljay Apr 13 '20 at 17:30
  • Then I suggest that you just use the `const_cast`. If you still think that it looks odd, just add a comment to explain why it's used and/or a link to somewhere (maybe this question) that explains it. I have a few comments with this link https://stackoverflow.com/a/123995/969365 in my code where I use that solution – simon Apr 13 '20 at 17:30
  • @Eljay in this example yes and the error is gone then, but not in my real code. It's probably best to tolerate that `const_cast` – schetefan24 Apr 13 '20 at 17:33
  • In your real code, can you overload `bool isConnected(Node* otherNode) const` and `bool isConnected(Node const* otherNode) const`, and have the latter do the `const_cast` to the former? That'll make the callsites cleaner, and sweep the ugly under the rug. – Eljay Apr 13 '20 at 17:36
  • Even in C++20, I think you would need to set up "transparent" hash and equality functor types to use either `contains` or `count`, instead of using default template arguments `std::hash` and `std::equal_to`. – aschepler Apr 13 '20 at 17:38
  • @Eljay for the caller side I used all possible const values and there is exactly one version of the function (my previous comment missed a const after the argument list and I don't find the edit button...). Adding an additional non-const function would be non-sense. I consider this as closed (I should be able to close my own post...) and stick to the `const_cast` – schetefan24 Apr 13 '20 at 17:59
  • You will still have to do `const_cast` even if you plan to use [contains](https://en.cppreference.com/w/cpp/container/unordered_set/contains) from C++20. – badola Apr 13 '20 at 22:17

1 Answers1

0

This is a limitation of C++, in my opinion.

Although there is clearly a trivial equality comparison between an int* and a const int*, the machinery of an associative container doesn't "know" that, unless you set up a transparent comparator for your map.

For an unordered associative container like yours, you'd also need a transparent hash (similar principle).

In this case, to be honest, and for simplicity, I'd just stick with the const_cast.

Asteroids With Wings
  • 16,164
  • 2
  • 17
  • 33