C++ map[]= memory ramifications

Asked by At

Let's say you have an unordered instance variable map that maps an integer to an object (like a time object). If you are overwriting that map and you use the standard overwrite like map_[5] = time.now() in a class function, where is that time.now object created? Is it in the stack where the function was and so when the function goes out of scope that time in the map goes out of scope? Or is it with the class objects so you can check that time later?

Basically, if you are writing to an instance field map in one function and you are storing objects you want to access later can you use normal objects or do you need to use pointers?

I haven't used C++ for a while and I'm trying to relearn the nuances and this is really confusing me. Thanks!

1 Answers

2
Miles Budnek On

The lifetime of objects contained in a std::map or std::unordered_map is the lifetime of the map. Keep in mind though that the object you assign to an element of a map isn't the same object as the one contained in the map.


For example, in this snippet:

std::map<int, std::chrono::system_clock::time_point> my_map;
auto now = std::chrono::system_clock::now();
my_map[10] = now;

There are actually at least two time_point objects involved:

  1. now
  2. my_map[10]

The line my_map[10] = now actually does quite a bit:

  1. Since no value exists in my_map for the key 10, map::operator[] default-constructs a new time_point object, inserts it into my_map, and returns a reference to it.
  2. The the assignment operator function of the returned time_point object is called and passed a reference to now.
  3. time_point::operator= adjusts the state of the time_point it was called on to mirror the state of the time_point passed to it.

It's the time_point object that's stored in the map whose lifetime is controlled by the map. now is a local variable, and as such its lifetime will end at the end of the scope in which it was declared.


For example, given the following class definition:

class Foo {
public:
    const std::chrono::system_clock::time_point& do_a_thing(int i);
private:
    std::map<int, std::chrono::system_clock::time_point> my_map;
};

The following definition of do_a_thing is invalid:

const std::chrono::system_clock::time_point& do_a_thing(int i) {
    auto now = std::chrono::system_clock::now();
    my_map[i] = now;
    return now;  // whoops, returning a reference to a function local
}

while the following is valid:

const std::chrono::system_clock::time_point& do_a_thing(int i) {
    auto now = std::chrono::system_clock::now();
    my_map[i] = now;
    return my_map[i]; // OK, the lifetime of my_map[i] is the same as my_map
}