Consider the following code:
std::map<int, int> m;
int &ref = m[0];
int *ptr = &m[0];
m.insert({1,2});
std::cout << ref; // #1
std::cout << *ptr; // #2
For an associative container like std::map
, the standard says:
The insert and emplace members shall not affect the validity of iterators and references to the container, ...
Which means #1
is definitely ok. However, I'm not so sure about #2
.
This question has been asked and answer over a decade ago.
The accepted answer says #2
is technically not allowed but will work in practice.
The consensus answer (with more than twice the number of up-votes than the accepted answer) says #2
is ok, by simply saying that the above standard quote implies pointers are not invalidated as well.
There are also at least half a dozen relatively more recent duplicates of this question, most of them with answers, and all of them say #2
is ok, usually by quoting the same standard text above.
I don't think this is correct. To my understanding, references are not pointers, and one can't be substituted for the other, whether they are implemented in terms of each other or not. As a comparison, here's what the standard says about the validity of referents to elements in unordered associative containers, upon rehashing:
Rehashing invalidates iterators, ..., but does not invalidate pointers or references to elements.
This explicitly guarantees pointers' validity, suggesting that the validity of references does not automatically imply it.
So does the language say #2
is ok? And is it implied by the validity of #1
?
As many comments said, this is probably a slight standard miswording and the standard implies pointers too. However, if you wish to get into the language lawyering of it, take this: a reference cannot be reassigned (not the standard per se but an official source I found: https://isocpp.org/wiki/faq/references#reseating-refs ). That is, once a reference points to an object, the reference will always point to that object. As said in the standard here: https://eel.is/c++draft/intro.object , an object occupies a given region of storage throughout its lifetime. As stated here: https://eel.is/c++draft/basic.compound , a pointer refers to the address of the first byte of its object. Thus, if the reference is still valid, because it can't be reassigned to another object, the object it points to has not ended its lifetime, so the memory it occupies is still valid and held by that object, and so the pointer pointing to the beginning of that memory is still valid. So, within the rules of the C++ abstract machine, as outlined in the standard, the pointer would still be valid.
To my understanding, references are not pointers, and one can't be substituted for the other, whether they are implemented in terms of each other or not.
I couldn't have said it better myself.
So does the language say #2 is ok? And is it implied by the validity of #1?
IMO one can't judge these matters by intent and assume that it must work, even if it seems to be some wording defect. As it stands this behavior must be regarded as implementation defined.
Your research is thorough and I believe there is no more to be done in that regard nor am I aware of any defect report about the matter, so IMO the approach is, as usual, if it's not in the standard one cannot assume it will work at all times even if it seems that it couldn't be any other way, and it does seem that way.
Ultimately a pointer to a variable is not the same as a variable reference and address-of operator is not the same of reference operator.
If you feel this should be addressed, you can always raise the issue yourself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With