Search code examples
c++vectorcopy

How to recreate a vector without unnecessary copies C++


I have a vector containing n complex objects. I want to perform a significant number of erasure, which would be extremely inefficient using .erase(). The way I thought I would do it is by creating an empty vector on the heap, filling it with copies of objects that are to be kept, and redirecting the original vector towards the new one, without a copy. And of course free the old .data().

Something like this:

vector<Obj>* newVptr = new vector<Obj>;
newVptr->reserve(newSize);
int newId = 0;
for (int i = 0; i < oldSize; i++) {
   if (isKept(oldV[i])) {
      (*newVptr)[newId] = oldV[i];  // deep copy of Obj
      newId++;
   }
}

then free oldV's array (.data()) and replace the internal pointer with newVptr. How ?

I cant just discard oldV and keep going with newV, as it is an attribute of a class.


Solution

  • There's almost never a need to allocate a std::vector dynamically, as they allocate dynamically internally.

    If you erase one-by-one, then that will be inefficient, yes. But the usual way to do this is using one of the std::remove* algorithms to move all the elements you want to keep to the front of the vector, and then call erase on the end.

    For example:

    oldV.erase(std::remove_if(oldV.begin(), oldV.end(), std::not_fn(isKept)),
               oldV.end());
    

    Or in C++20:

    std::erase_if(oldV, std::not_fn(isKept));
    

    If you really want to copy the kept elements instead you could do:

    std::vector<Obj> newV;
    newV.reserve(oldV.size());
    std::copy_if(oldV.begin(), oldV.end(), std::back_inserter(newV), isKept);
    oldV = std::move(newV);
    

    That std::move at the end would do the equivalent of your pointer swap idea.