3

According to std::map documentation, it stores the key-value pairs in std::pair<const Key, Value>, so the keys in the map are const.

Now imagine that I have an std::map where the keys are the pointers to some objects.

struct S {}; struct Data {}; using MyMap = std::map<S*, Data>; 

Let's also assume there is a function foo that accepts S* parameter.

void foo(S* ptr) { /* modify the object (*ptr) */ } 

Now, the question is: when I iterate over MyMap with range-based for-loop, I am able to pass the map element key to foo:

MyMap m = getMyMapSomehow(); for (auto elem : m) { static_assert(std::is_const<decltype(elem.first)>::value, "Supposed to be `const S*`"); foo(elem.first); // why does it compile? } 

So, even though my static_assert succeeds (so I assume that the type of elem.first is const S*), the call to foo compiles fine, and therefore it look as if I'm able to modify the object behind pointer-to-const.

Why am I able to do that?

P.S. Here's a live example at Coliru that illustrates my point. For brevity, I use int instead of S and Data.

4
  • You're making a copy of the element; you can do whatever you want with that personal copy... Commented Jul 26, 2017 at 16:46
  • 2
    Don't confuse a pointer to const with an immutable pointer! Commented Jul 26, 2017 at 16:47
  • @KerrekSB ah, indeed! My brain confuses me at the end of my working day :) Thanks! Commented Jul 26, 2017 at 16:51
  • Happens to the best :-) Commented Jul 26, 2017 at 16:53

3 Answers 3

5

so I assume that the type of elem.first is const S*

No. The key stored in map is const, that means for std::map<S*, Data>, the key will be S* const (i.e. const pointer), not const S* (i.e. pointer to const). So it's fine to pass it to foo(S* ptr), the const pointer itself will be copied to the parameter.

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

7 Comments

You are right, I mixed const T* with T* const. Such things sometimes happen at the end of the work day :-) Thanks!
@VasiliyGalkin: One note: though const X* and X const* are the same thing, making a habit of writing X const* is helpful especially when working with template and typedefs if you think visually .
@Nawaz nah, I wouldn&#39;t agree to that. But I wouldn&#39;t want to start yet another const T* vs T const* holywar either ;)
@VasiliyGalkin: Think visually: using pint = int*; then what does const pint mean? const int*? or ... ? :-/
@Nawaz I strongly believe that typedefs for raw pointers to some type is in general very bad practice. So let's just agree that we disagree on this matter ;)
|
3

Here is a simpler example, see if you can work it out:

void f(int); // takes a non-const int int main() { std::set<int> s; // elements are const ints for (auto n : s) { f(n); // OK!? } } 

4 Comments

Ah, indeed! I didn't pay enough attention to the fact that the key type is T* const rather than const T*. Thanks!
auto n makes a copy and then f(int) takes a copy. So I didn't understand what this answer is trying to explain by using copies!
@Nawaz: this is basically the same situation as the OP faces: A function taking a non-const value can be called with a (copy of a) const set element. Note that the code wouldn't work if you had f(int&) and for (auto& n : s). I understand there's a second level of confusion, but I think if you can wrap your head around the simple example, you have enough differential information to ask the right question about the original situation.
@KerrekSB: "Note that the code wouldn't work if you had f(int&) and for (auto& n : s)". Both of them need not to make copy. Having either f(int) or auto n would work!
1

std::map<K, V>::value_type is std::pair<const K, V>, as you mentioned. So what is const K when S* is substituted for K? The answer, which might surprise you, is not const S*. but rather S* const.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.