std::map, find function does not accept const Key* even though docs and error message seems to say it does

Nov 26, 2011 at 9:54am
When using std::map, find function does not accept const Key* even though docs and error message seems to say it does. Why is this so? Or can you find something wrong with my code below? (Running on Linux)

1
2
3
4
5
typedef std::map<MyKey*, MyValue, MyCompare, std::allocator<std::pair<MyKey* const, MyValue> >> MyMap;

void somefunc(const MyKey* myKey) {
  MyMap::iterator myMapIt = mMyMap.find(myKey);
}



//>>>>>>>>> Error messages from the compiler
no matching function for call to 'std::map<MyKey*, MyValue, MyCompare, std::allocator<std::pair<MyKey* const, MyValue> > >::find(const MyKey*&)'

candidates are: typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const _Key&) [with _Key = MyKey*, _Tp = MyValue, _Compare = MyCompare, _Alloc = std::allocator<std::pair<MyKey* const, MyValue> >] <near match>



1
2
3
// >>>>>>>>>>> To me they seem VERY equal
std::map<MyKey*, MyValue, MyCompare, std::allocator<std::pair<MyKey* const, MyValue> >>::find(const MyKey*&)
std::map<MyKey*, MyValue, MyCompare, std::allocator<std::pair<MyKey* const, MyValue> >>::find(const MyKey*&) <near match>
Last edited on Nov 26, 2011 at 10:25am
Nov 26, 2011 at 10:15am
A map is defined to by Key/Value pairs. You'll find nothing in the definition that does Key*.
http://www.sgi.com/tech/stl/Map.html
Nov 26, 2011 at 10:24am
Thanks for your reply, however, I have no idea what you mean. You have to explain more.
It works perfectly fine to have a Key that is a pointer.

I noticed that if I write
1
2
3
void somefunc(MyKey* const myKey) {
  MyMap::iterator myMapIt = mMyMap.find(myKey);
}


the code compiles just fine. However, this only promises the user of somefunc that the somefunc code will not change the pointer value itself - it doesn't say anything about changing the actual values that the pointer points to. I feel this is wrong. But perhaps what you really mean, kbw, is that std::map was not designed for use with pointers as keys, even if you can use it like that if you want to.
Nov 26, 2011 at 10:31am
You don't use it like that. Think of the iterator as the pointer.
1
2
3
4
void somefunc(const MyKey &myKey)
{
    MyKey::iterator myMapIt = mMyMap.find(myKey);
}

Last edited on Nov 26, 2011 at 10:32am
Nov 26, 2011 at 2:12pm
How do you mean? In my case MyKey does not support iterators.
Last edited on Nov 26, 2011 at 2:13pm
Nov 26, 2011 at 2:57pm
I think what might be going on is that std::map::find() takes a const reference. If you pass it a MyKey* then it is receiving a const reference to a MyKey*. That is not the same as a const MyKey*. The type (MyKey*) is not itself const, only the reference to it in the find() function.

EDIT: See if this works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct MyKey
{

};

std::map<MyKey*, std::string> myMap1;

void somefunc1(MyKey* const& myKey)
{
	std::map<MyKey*, std::string>::iterator myMapIt = myMap1.find(myKey);
}

std::map<MyKey const*, std::string> myMap2;

void somefunc2(MyKey const* const& myKey)
{
	std::map<MyKey const*, std::string>::iterator myMapIt = myMap2.find(myKey);
}
Last edited on Nov 26, 2011 at 3:04pm
Nov 26, 2011 at 6:29pm
How do you mean? In my case MyKey does not support iterators.
MyKey isn't the problem. You've passed the wrong type to find().
Nov 27, 2011 at 7:19am
Galik,

thanks for the clarification. However, then I think the compiler error message is a bit misleading, or actually wrong

candidates are: typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const _Key&) [with _Key = MyKey*, _Tp = MyValue, _Compare = MyCompare, _Alloc = std::allocator<std::pair<MyKey* const, MyValue> >] <near match>


The "::find(const _Key&)" part, having "_Key = MyKey*" does translate to

::find(const MyKey*&)

and not

::find(MyKey* const&)

Otherwise I think the error should have read

::find(_Key const&)

Don't you agree?
Last edited on Nov 27, 2011 at 7:20am
Nov 27, 2011 at 9:32am

Well... this is how I think it works (I admit I get confused over this too).

The problem is how C resolves the type. Even though logically inserting MyKey* between the const and the & looks like it has become the subject of a const& it doesn't quite work that way.

What happens is that the const latches on to the * changing the semantics. If you went through a typedef you could avoid that:
1
2
3
typedef MyKey* MyKeyPtr;

find(const MyKeyPtr& myKey); // works fine 

But without the typedef, the introduction of the * symbol causes the const to apply to it rather than the &.

The way to spell out to the compiler that you want the const to remain on the & rather than the * is:
 
find(MyKey* const& myKey); // works fine 


The thing to remember is that it doesn't matter which side of the identifier the const goes but it does matter which side of the * or & it goes.

So to make the pointer or reference const you can do:
1
2
3
const MyKey* mp1; // these two...

MyKey const* mp2; // ...are the same 

Same thing with reference:
1
2
3
const MyKey& mr1; // these two...

MyKey const& mr2; // ...are the same 

But when you want a reference to a pointer:
 
MyKey*& mpr;

Choosing which one is const is more tricky. Making the pointer const is easy because it is closest to the identifier:
1
2
3
const MyKey*& mpr1; // these two...

MyKey const*& mpr2; // ...are the same 

But making the reference const is harder because you only have one option available to you:
 
MyKey* const& mpr;

Its all down to order of precedence which is designed to allow the programmer to express all possible combinations of constness.

(hopefully I didn't make any errors in that explanation)
Nov 27, 2011 at 11:31am
However, then I think the compiler error message is a bit misleading
C++ compiler errros that involve STL have almost always been misleading. As I understand it, it's a work in progress.
Topic archived. No new replies allowed.