Search code examples
c++stdmapstd-function

std::map with std::function value calls destructor 4 times but only construct one object


I run this very simple example on Visual Studio (2015). for some reason the Functor class destructor is invoked 4 times. I assume the implementation invokes the auto-generated copy-constructor a few times but I suspect there might be a bug here. If I implement a copy constructor myself, I get only 3 destructor calls corresponding to 1 default constructor call and 2 copy constructor calls.

#include <functional>
#include <map>
#include <iostream>

using namespace std;

class Functor
{
public:

    Functor()
    {
        cout << "Functor::Functor()" << endl;
    }

    Functor& operator=(const Functor& rhs) = delete;

    ~Functor()
    {
        cout << "Functor::~Functor()" << endl;
    }

    void operator()()
    {
        cout << "Functor::operator()" << endl;
    }

};

int main()
{
    std::map<int, std::function<void(void)>> myMap;

    myMap[1] = Functor();

    return 0;
}

output:

Functor::Functor()
Functor::~Functor()
Functor::~Functor()
Functor::~Functor()
Functor::~Functor()

and if I implement the copy constructor myself:

Functor(const Functor& that)
{
    cout << "Functor::Functor(const Functor&)" << endl;
}

output:

Functor::Functor();
Functor::Functor(const Functor&)
Functor::Functor(const Functor&)
Functor::~Functor()
Functor::~Functor()
Functor::~Functor()

can someone explain which objects are being destructed? what is happening here?


Solution

  • If you implement your own copy constructor, the move-constructor is suppressed, so different overloads are selected.

    Try this:

    struct Reporter
    {
        Reporter()                           { cout << "Default constructor\n"; }
        Reporter(const Reporter&)            { cout << "Copy constructor\n"; }
        Reporter(Reporter&&)                 { cout << "Move constructor\n"; }
        Reporter& operator=(const Reporter&) { cout << "Assignment operator\n"; return *this; }
        Reporter& operator=(Reporter&&)      { cout << "Move Assignment operator\n"; return *this; }
        ~Reporter()                          { cout << "Destructor"; }
    };
    

    And have the class you are interested in inherit from it.

    The four places in the original is probably:

    • myMap[1]
      this creates a default-constructed object, that gets destroyed when overridden.
    • Functor()
      this creates a temporary that gets destroyed at the end of the full expression.
    • std::function<void(void)> the constructor of std::function probably takes it's argument by value, to enable move-semantics
    • } your map goes out of scope