This is absolutely correct behavior. @Cassio Neri pointed out why this is required by the standard.
Short:
"std :: vector :: erase (iterator position) does not necessarily invoke the corresponding element destructor" [Op; Header] , but the destructor is called, which processes the data of the corresponding elements that were transferred to another object (either using the move to move mechanism, or through RAII to a temporary instance).
Long
Why you do not need to rely on the i-th destructor to be called.
I will give some tips why you should not worry at all which destructor is called in this case.
Consider the following small class
class test { int * p; public: test (void) : p(new int[5]) { cout << "Memory " << p << " claimed." << endl; } ~test (void) { cout << "Memory " << p << " will be deleted." << endl; delete p; } };
If you correctly control the assignment of moving an object, there is no need to worry about which destructor is called correctly.
test& operator= (test && rhs) { cout << "Move assignment from " << rhs.p << endl; std::swap(p, rhs.p); return *this; }
Your move assignment statement should pass the state of the object “overwritten” to the object that is “moved from” ( rhs here), so the destructor will take the right action (if there is anything that the destructor needs to take care of). Perhaps you should use something like the "swap" function to pass to you.
If your object does not move, you will have to handle the “cleanup” (or any action based on the current state of the object) of the erased object in the copy destination operation before copying new data to the object.
test& operator= (test const &rhs) { test tmp(rhs); std::swap(p, tmp.p); return *this; }
Here we use RAII and again swap (which can still be a member function, but the test has only one pointer ...). The tmp destructor will make things cozy.
Do a little test:
#include <vector> #include <iostream> using namespace std; class test { int * p; public: test (void) : p(new int[5]) { cout << "Memory " << p << " claimed." << endl; } test& operator= (test && rhs) { cout << "Move assignment from " << rhs.p << endl; std::swap(p, rhs.p); return *this; } ~test (void) { cout << "Memory " << p << " will be deleted." << endl; delete p; } }; int main (void) { cout << "Construct" << endl; std::vector<test> v(5); cout << "Erase" << endl; v.erase(v.begin()+2); cout << "Kick-off" << endl; return 0; }
Results in
Construct Memory 012C9F18 claimed. Memory 012CA0F0 claimed. Memory 012CA2B0 claimed.
Each declared memory cell will be released correctly if the assignment (or copy) operation passes critical properties to the object that will be destroyed.
Each destructor that relies on the internal state of an object will be called with the corresponding object around if your assignment operations are properly designed.