Home > Enterprise >  Is there a way to call the destructor of a replaced element when using std::replace?
Is there a way to call the destructor of a replaced element when using std::replace?

Time:11-22

I have a vector of objects where I want to replace one element by another. Using std::replace seemed appropriate, so I gave it a try.

I was concerned about the whereabouts of the replaced element though, so I did some tests :

#include <iostream>
#include <vector>
#include <algorithm>

class A {
    public:
    A(const int ii) : i(ii) { std::cout << "Constructor " << i << std::endl;}
    ~A(){std::cout << "Destructor " << i << std::endl;}

    auto operator ==(const A& a){ return i == a.i;}
    int i;
};

int main()
{
    
    std::vector<A> v;
    v.reserve(4);
    v.emplace_back(10);
    v.emplace_back(20);
    v.emplace_back(40);
    v.emplace_back(30);
    for(const auto& elt : v){
        std::cout << elt.i << ' ';
    }
     std::cout << std::endl;

    std::replace(v.begin(), v.end(), v[2], A(25));
    for(const auto& elt : v){
        std::cout << elt.i << ' ';
    }
     std::cout << "End of program" << std::endl;
}

Third element of the vector is replaced, but as you can see the replaced object destructor isn't called :

Constructor 10
Constructor 20
Constructor 40
Constructor 30
10 20 40 30 
Constructor 25
Destructor 25
10 20 25 30 End of program
Destructor 10
Destructor 20
Destructor 25
Destructor 30

As the behaviour is the same on every compiler, I suppose it's intended, but is there a way to have the destructor called in that case ?

Testing example : https://godbolt.org/z/YTaKcrTEn

Thanks !

CodePudding user response:

No. replace uses assignment, and assignment requires a live object.

If your type has a meaningful destructor (ie: rule of 5), and your type is assignable, then assignment ought to be effectively equivalent to calling the destructor and then doing placement-new on the object.

CodePudding user response:

I think you may have a misunderstanding of what a destructor does.

The destructor takes an object, and turns it into "not an object" (just raw memory). That is the opposite of a constructor, which takes raw memory and turns it into an object.

In your code, you assign a new value to an object (via std::replace). That does not create new objects, nor does it destroy any objects - so there are no constructor or destructor calls.

CodePudding user response:

From cppreference on std::replace:

Because the algorithm takes old_value and new_value by reference, it can have unexpected behavior if either is a reference to an element of the range [first, last).

Therefore, this is wrong:

std::replace(v.begin(), v.end(), v[2], A(25));

Indeed, we can't choose old_value to be a reference to v[2] as done here. Consider instead:

A to_replace{v[2]};  // copy
std::replace(v.begin(), v.end(), to_replace, A(25));

You can also force the copy without using an additional variable (as Staz suggests):

std::replace(v.begin(), v.end(), A{v[2]}, A(25));
  • Related