在结构体的STL Map中,为什么"[]"运算符会导致结构体的dtor被多次调用2次?

Mon*_*urd 8 c++ constructor stl copy-constructor temporary-objects

我创建了一个简单的测试用例,展示了我在一个更大的代码库中注意到的奇怪行为.这个测试用例如下.我依靠STL Map的"[]"运算符来创建指向这种结构的映射中的结构的指针.在下面的测试用例中,线...

TestStruct *thisTestStruct = &testStructMap["test"];
Run Code Online (Sandbox Code Playgroud)

...给我指针(并在地图中创建一个新条目).我注意到的奇怪之处在于,这一行不仅会导致地图中的新条目被创建(因为"[]"运算符),但由于某种原因,它会导致结构体的析构函数被多次调用两次.我显然错过了一些东西 - 非常感谢任何帮助!谢谢!

#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";
    }
};
Run Code Online (Sandbox Code Playgroud)

上面的代码输出以下内容......

/*
Marker One
TestStruct Constructor!             //makes sense
TestStruct Destructor!               //<---why?
TestStruct Destructor!               //<---god why?
Marker Two
TestStruct Destructor!               //makes sense
*/
Run Code Online (Sandbox Code Playgroud)

...但我不明白是什么原因导致TestStruct的析构函数的前两次调用?(我认为最后一个析构函数调用是有意义的,因为testStructMap超出了范围.)

AnT*_*AnT 18

功能std::map<>::operator[]相当于

(*((std::map<>::insert(std::make_pair(x, T()))).first)).second
Run Code Online (Sandbox Code Playgroud)

表达式,如语言规范中所指定.正如您所看到的,这涉及默认构造类型的临时对象T,将其复制到std::pair对象中,稍后将其复制(再次)到地图的新元素中(假设它已经不存在).显然,这会产生一些中间T对象.您在实验中观察到的破坏这些中间物体.您错过了他们的构造,因为您没有从班级的复制构造函数生成任何反馈.

中间对象的确切数量可能取决于编译器优化功能,因此结果可能会有所不同.


Mic*_*urr 8

你有一些看不见的副本:

#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( TestStruct const& other) {
        std::cout << "TestStruct copy Constructor!\n";
    }

    TestStruct& operator=( TestStruct const& rhs) {
        std::cout << "TestStruct copy assignment!\n";
    }

    ~TestStruct(){
        std::cout << "TestStruct Destructor!\n";
    }
};
Run Code Online (Sandbox Code Playgroud)

结果是:

Marker One
TestStruct Constructor!
TestStruct copy Constructor!
TestStruct copy Constructor!
TestStruct Destructor!
TestStruct Destructor!
Marker Two
TestStruct Destructor!
Run Code Online (Sandbox Code Playgroud)


jus*_*tin 5

将以下内容添加到TestStruct的界面:

TestStruct(const TestStruct& other) {
    std::cout << "TestStruct Copy Constructor!\n";
}   
Run Code Online (Sandbox Code Playgroud)