Reputation: 7506
Today I saw my boss's code which uses a const reference as a map
's value type.
Here's the code:
class ConfigManager{
public:
map<PB::Point, const PB::WorldPoint&, compare_point> world_point;
//the rest are omitted
};
(PB
is Google Protobuf, we are using the Protobuf library. I don't know much about it or if it's relevant to the question. )
What this class does is that it reads some config files and put it into some map
s for searhing.
At first I was surprised because I haven't seen a map
with a reference as its value, which is e.g. map<int, classA&> aMap
.
So then I searched on SO and these 2 questions tell me that I can't do that.
C++: Is it possible to use a reference as the value in a map?
STL map containing references does not compile
Then I tried this code, indeed it doesn't compile:
struct A {
int x = 3;
int y = 4;
};
map<int, A&> myMap;
int main() {
A a;
myMap.insert(make_pair(1, a));
}
But if I change map<int, A&> myMap;
to map<int, const A&> myMap;
, it compiles.
Yet another problem occured. With map<int, const A&> myMap;
, I can't use []
to get the pair, but I can use map.find()
.
(My boss told me to use map.find()
after I told him using[]
can't compile).
struct A {
int x = 3;
int y = 4;
};
map<int, const A&> myMap;
int main() {
A a;
myMap.insert(make_pair(1, a));
//can't compile
cout << myMap[1].x << " " << myMap[1].y << endl;
//can work
//auto it = myMap.find(1);
//cout << it->second.x << " " << it->second.y << endl;
}
So till here I was thinking my boss was correct. His code was correct.
The last story is that I showed the code to some online friends. And they noticed a problem.
#include <map>
#include <iostream>
#include <string>
using namespace std;
struct A {
int x = 3;
int y = 4;
~A(){
cout << "~A():" << x << endl;
x = 0;
y = 0;
}
};
map<string, const A&> myMap;
int main() {
A a;
cout << a.x << " " << a.y << endl;
myMap.insert(make_pair("love", a));
a.x = 999;
cout << "hello" << endl;
auto s = myMap.find("love");
cout << s->second.x << " " << s->second.y << endl;
}
The output is:
3 4
~A():3
hello
0 0
~A():999
If I understand the output correctly(correct me if I get it wrong), it indicates that:
make_pair("love", a)
creates an object pair<"love", temproray copy of a>
. And the pair gets inserted
into myMap
.temporary copy of a
gets destructed immediately. To me, it means the memory of the temporary copy of a
is now not owned by anyone and it is now a free space of memory that can be filled with any values, if I understand correctly.So now I am getting confused again.
My questions are:
What happens to the Code Example3? Is my understanding correct? Why does temporary copy of a
get destructed right after the statement? Isn't using a const reference can extend a temporary's lifetime? I mean, I think the it should not get destructed till main
finishes.
Is my boss's code incorrect and very dangerous?
Upvotes: 2
Views: 2045
Reputation: 29023
What happens to the Code Example3? Is my understanding correct?
Your explanation is close. The std::pair
that is returned by std::make_pair
is the temporary object. The temporary std::pair
contains the copy of a
. At the end of the expression the pair is destroyed, which also destroys its elements including the copy of a
.
Why does temporary copy of a get destructed right after the statement? Isn't using a const reference can extend a temporary's lifetime? I mean, I think the it should not get destructed till main finishes.
The temporary here is the result of std::make_pair
which is being used as an argument to the member function insert
. The relevant rules that apply here are :
Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference, with the following exceptions:
- [...]
- a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call [...]
- [...]
The full expression containing the function call is the expression myMap.insert(make_pair(1, a));
This means that the lifetime of the result of std::make_pair
ends after the function return, including the A
it contains. The new std::map
element will refer to the A
in the temporary std::pair
which will become dangling once insert
returns.
Is my boss's code incorrect and very dangerous?
Yes, myMap
contains a dangling references.
Upvotes: 1
Reputation: 4201
You may use std:: unique_ptr<A>
instead. Then emplace
instead of insert
:
using value_t=std:: unique_ptr<A>;
std::map<int, value_t> myMap;
myMap.emplace(1,new A);
myMap[1]=new A{5,6};
myMap[1]->x=7;
more on std:: unique_ptr<A>
:
https://en.cppreference.com/w/cpp/memory/unique_ptr
Upvotes: 1
Reputation: 87959
Why does temporary copy of a get destructed right after the statement?
Because (in most cases) that's how temporaries work. The live until the end of the statement in which they are created. The extension to a temporaries lifetime doesn't apply in this case, see here. The TLDR version is
In general, the lifetime of a temporary cannot be further extended by "passing it on": a second reference, initialized from the reference to which the temporary was bound, does not affect its lifetime.
can I use const reference as a map's value type?
Yes as long as you realise that adding a const reference to a map has no effect on the lifetime of the object being referred to. Your bosses code is also incorrect because the temporary returned by make_pair
is destroyed at the end of the statement.
Upvotes: 2