1

There is a map of a type std::map<A*, B*> m that describes correspondence between objects of type A and objects of type B. There is a function int exctractInfo(const A *a) that needs to read some info from an object of type B which corresponds to a given object of type A. This is a semantically constant operation, nothing needs to be changed, we just need to read some info, but the problem is that C++ doesn't allow access to map m via a pointer to const.

Consider the following code:

#include <map> class A { }; class B { int info_; public: int info() const { return info_; } }; std::map<A*, B*> m; int exctractInfo(const A *a) { auto it = m.find(a); if (it != m.end() && it->second) { return it->second->info(); } return -1; } int main () { return 0; } 

Here's a link to online compiler for this code. I get the following error:

error: invalid conversion from 'const A*' to 'std::map::key_type {aka A*}' [-fpermissive]

Now I see two solutions:

  1. Rewrite the type std::map<A*, B*> as std::map<const A*, B*>, as I have access to the source code, but that is basically a type of a library object, and a lot of code depend on it, so it will have to be changed, thus changing the map type is really undesirable;

  2. Use const cast like this: auto it = m.find(const_cast<A*>(a));, which also doesn't seem like a good solution, more of a hack.

I don't understand why it doesn't work. If the key is std::string or int, for example, I can access std::map<std::string, B*> via const std::string just fine. So what's wrong with my example? Is there a proper way to handle this situation?

4
  • But you don't have a const pointer, you have a pointer to const. i.e. the map will handle A * const just fine, but not a A const *. My guess is extractInfo could take a A * const instead. Commented Jun 12, 2020 at 16:01
  • The third solution is to use a transparent comparator: std::map<A*, B*, std::less<>> m; Commented Jun 12, 2020 at 16:01
  • @cigien, yes, you're right, sorry, I named it wrong, it's a pointer to const of course. The problem is that I get those pointers from a code that I can't change, so even if I change signature of extractInfo, I'd still have to discard const qualifier at some point. The point is that object A is not allowed to be changed, that's why it's const. But the const *A and *A will hold the same address, so it's the same key, so technically it should be enough the access a value of a map without changing anything along the way. Commented Jun 12, 2020 at 16:13
  • @PiotrSkotnicki, because I didn't want to mess around with the old code where this map is declared, and also because it would mean changing a lot of code that uses this map in a project. Plus I read somewhere that making a key const is moot, since, as you said, keys are immutable anyway. But now I see how it can come back and bite you later. Commented Jun 12, 2020 at 16:32

1 Answer 1

2

I don't understand why it doesn't work. If the key is std::string or int, for example, I can access std::map via const std::string just fine. So what's wrong with my example?

Because there is a significant difference btw constant pointer to non constant data and non constant pointer to constant data. Your map has first as a key, you try to pass second. So if you are pre C++14 only viable solution would be const_cast I am afraid (beside changing key type of course). If you can use C++14 or later then "transparent comparison" is available as stated in std::map::find() example. For it to work you would need to declare your map like this:

std::map<A*, B*,std::less<>> m; 

live example

Sign up to request clarification or add additional context in comments.

9 Comments

It does seem unfortunate that these antics are required, given that a conversion from A* to const A* ought to be value-transparent and incur literally zero runtime cost :(
@AsteroidsWithWings I don't think that transparent comparison would invoke runtime cost as that code is templatized.
I didn't say it would!
@AsteroidsWithWings then I do not quite understand your concern. Conversion from const to non-const is a dangerous operation and so not allowed to be done implicitly. You can use const_cast to override that and that would not have any runtime const. OP does not like casting so there is alternative solution for that.
Conversion from non-const to const is just fine, value-transparent and zero cost. A* ptr1 = foo(); const A* ptr2 = ptr1; So it's a shame find can't do this for us when the map key is ptr2 and the input is ptr1. We need "antics" like explicitly creating a transparent comparator that IMO ought to be implicit.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.