1

I have a set of pointers to a class:

std::set<myclass*> myset

In a function that gets a const myclass* ss argument, I want to search whether myset contains ss:

bool item_in_set( const myclass *ss ) {
  return myset.find( ss ) != myself.end();
}

This fails with an 'argument would lose const qualifier' error. Although it makes sense, I would expect that std::set has a way to search for items using a const qualifier but I can't find anything. To make this work I used:

bool item_in_set( const myclass *ss ) {
  return myset.find( const_cast<myclass*>( ss ) ) != myself.end();
}

This works but is clunky and I'm concerned that I'm missing something.

Is there a better way to do this, or any risks with my approach?

I don't want to change the signature of item_in_set() as it's pretty far in the call stack of other functions with a const myclass* signature (I have simplified the names in this example).

I'm using C++17.

xpapad
  • 4,376
  • 1
  • 24
  • 25
  • Storing pointers as keys in sets or maps is problematic, since it's the actual *pointer* that's the key, not the object the pointers are pointing to. So if you have two `myclass` objects: `myclass a, b;` and they otherwise compare equal (i.e. `a == b` is true, assuming there's a suitable `==` operator defined), then `&a` will not be equal to `&b`. And it's those pointers that are stored in the set. – Some programmer dude Nov 12 '22 at 11:03
  • 1
    A constant object can't be converted to a non-constant object without an explicit `const_cast`. The same goes for pointers to objects: Pointers to constant object (i.e. `const myclass*`) can't be implicitly converted to a pointer to a non-constant object (i.e. `myclass*`). You must either change the signature (which isn't practical in your use-case) or use the `const_cast` (which is safe for this use-case). – Some programmer dude Nov 12 '22 at 11:53

2 Answers2

0

Switching to a transparent comparator fixes this:

std::set<myclass*, std::less<>> myset;

It's a good idea to use it by default for all your sets/maps.

It makes .find() a template, accepting any type comparable with the element type.

For some reason, it doesn't affect some of the functions (such as .at()); for those I'd continue using const_cast.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
-1

Wow, really wierd problem.

One workaround is to use std::find(...) although it might affect performance.

    return std::find( myset.begin(), myset.end(), ss ) != myset.end();
Lasersköld
  • 2,028
  • 14
  • 20
  • 1
    Doing a linear search just to a avoid a `const_cast` is silly. – HolyBlackCat Nov 12 '22 at 11:05
  • @HolyBlackCat Thats your opinion. I never said it was a perfect idea, but sometimes you do not have control over the type and performance of the find operation is not a problem. Then it would be a adequate solution. – Lasersköld Nov 12 '22 at 11:07