Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid const cast for map access?

Tags:

I have the following issue:

std::map<A*,double> map;

void getColor(A const * obj){
    double d = map[obj]; // does not compile wihtout const_cast<A*>(obj)
    // do something
}

I have a map std::map (somewhere) which stores pointers to objects A. I have a function getColor which does not manipulate objects A and so takes a pointer to a const A as input.

The function getColor will not compile without using a const_cast.

The const cast is a design issue, but I don't know how to circumvent it if I don't want to make the keys in map const.

Any help appreciated.

like image 641
Gabriel Avatar asked Nov 24 '16 13:11

Gabriel


2 Answers

There's two possible scenarios here:

  1. The function knows/expects that obj is already present in the map, and you're using [] for convenience.

  2. You're using [] for its full potential, i.e. you expect it to add obj to the map if not already there.

In situation 2, you have an error in the getColor signature. Since it can potentially pass obj to a place where it will be stored as A*, it is wrong for it to accept a const A* only. Note that even if a function doesn't modify an object itself but passes it on somewhere where it can be modified, it is effectively modifying it indirectly and should therefore take it as non-const.

In situation 1, it depends on your C++ version. C++14 introduced a template overload of find and related member functions of std::map which takes anything comparable with Key instead of only Key. You could therefore modify the function like this:

void getColor( A const * obj){
    doubel d = map.find(obj)->second;
    // do something
}

Note that for this to work, you also need to change the map's type to use a transparent comparator: std::map<A*,double, std::less<>> map; (as first pointed out by @Leon's answer).

If you're stuck with C++11 or earlier, you're out of luck and you'll have to live with the const_cast. Note that with a suitable comment, a const_cast is perfectly safe and acceptable in this case (not to mention the only way to proceed without changing the type of map). Again, you should use find or perhaps at instead of [], since you do not want to insert into the map.

like image 57
Angew is no longer proud of SO Avatar answered Oct 01 '22 06:10

Angew is no longer proud of SO


If you can afford switching to C++14 then you can configure your map to use a transparent comparator (this will work since a const pointer can be compared with a non-const pointer):

std::map<A*,double, std::less<>> map;
//                  ^^^^^^^^^^^
//                  enable transparent comparator on this map

void getColor( A const * obj){
    auto it = map.find(obj);
    assert(it != map.end());
    double d = it->second;
    // do something
}

Note that you will have to use std::map::find() instead of std::map::operator[], since the latter doesn't have a transparent version.

like image 42
Leon Avatar answered Oct 01 '22 05:10

Leon