Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In an STL Map of structs, why does the "[ ]" operator cause the struct's dtor to be invoked 2 extra times?

I've created a simple test case exhibiting a strange behavior I've noticed in a larger code base I'm working on. This test case is below. I'm relying on the STL Map's "[ ]" operator to create a pointer to a struct in a map of such structs. In the test case below, the line...

TestStruct *thisTestStruct = &testStructMap["test"];

...gets me the pointer (and creates a new entry in the map). The weird thing I've noticed is that this line not only causes a new entry in the map to be created (because of the "[ ]" operator), but for some reason it causes the struct's destructor to be called two extra times. I'm obviously missing something - any help is much appreciated! Thanks!

#include <iostream>
#include <string>
#include <map>

using namespace std;
struct TestStruct;

int main (int argc, char * const argv[]) {

    map<string, TestStruct> testStructMap;

    std::cout << "Marker One\n";

    //why does this line cause "~TestStruct()" to be invoked twice?
    TestStruct *thisTestStruct = &testStructMap["test"];

    std::cout << "Marker Two\n";

    return 0;
}

struct TestStruct{
    TestStruct(){
        std::cout << "TestStruct Constructor!\n";
    }

    ~TestStruct(){
        std::cout << "TestStruct Destructor!\n";
    }
};

the code above outputs the following...

/*
Marker One
TestStruct Constructor!             //makes sense
TestStruct Destructor!               //<---why?
TestStruct Destructor!               //<---god why?
Marker Two
TestStruct Destructor!               //makes sense
*/

...but I don't understand what causes the first two invocations of TestStruct's destructor? (I think the last destructor invocation makes sense because testStructMap is going out of scope.)

like image 509
Monte Hurd Avatar asked Oct 25 '10 19:10

Monte Hurd


1 Answers

The functionality of std::map<>::operator[] is equivalent to

(*((std::map<>::insert(std::make_pair(x, T()))).first)).second

expression, as specified in the language specification. This, as you can see, involves default-constructing a temporary object of type T, copying it into a std::pair object, which is later copied (again) into the new element of the map (assuming it wasn't there already). Obviously, this will produce a few intermediate T objects. Destruction of these intermediate objects is what you observe in your experiment. You miss their construction, since you don't generate any feedback from copy-constructor of your class.

The exact number of intermediate objects might depend on compiler optimization capabilities, so the results may vary.

like image 74
AnT Avatar answered Oct 05 '22 08:10

AnT