Search code examples
c++for-loopunique-ptrraiiownership

How to take ownership of an object while looping over std::vector of std::unique_ptr using a range based for loop?


I have a std::vector<std::unique_ptr<Kind>> which I want to clean up while it is being iterated upon, without explicitly calling the destructor of its members (.reset()).

The Kind is a heavy struct and its size increases during the iteration. The next object doesn't need to know about previous objects so I'd like to clean up an iterand when its not needed.

I know vector will clean up in the end, but by then, lots of Kind and their dynamically allocated memory adds up. I'm trying to reduce peak memory to just one element.

I want to avoid reset since other developers may not know about the dynamic allocation, forget calling reset in the end of the loop and cost memory penalty.

I cannot create a copy,

for(std::unique_ptr<Kind> t : store)

I cannot move it like

for(std::unique_ptr<Kind> &&t : store)

Then how do I do it ?

#include <iostream>
#include <vector>

struct Kind{
    char a;
    char *array;
    Kind(const char c): a(c)
    {
    }
    ~Kind(){
      free(array); // internal custom deallocator.
    }
};

int main() {
    std::vector<std::unique_ptr<Kind>> store;
    store.push_back(std::make_unique<Kind>('y'));
    store.push_back(std::make_unique<Kind>('z'));

    for(std::unique_ptr<Kind> &t : store){
        // increase size of Kind.array.
        std::cout << t->a;
        // Use the Kind.array
        // clean up t automatically.
    }
    return 0;

}

Solution

  • Example of moving the element out of the vector.

    int main() {
        std::vector<std::unique_ptr<Kind>> store;
        store.push_back(std::make_unique<Kind>('y'));
        for(std::unique_ptr<Kind> &t : store){
            auto tmp = std::move(t); // leaving a valid but empty entry in store
            std::cout << tmp->a;
            // clean up t automatically.
            // tmp runs out of scope and cleans up
        }
        return 0;
    }
    

    In effect not much different from the reset, but might be relevant for what you actually do in your real program.