How to push_back a "unique_ptr<Base>&" to "vector<unique_ptr<Derived>>"

Viewed 171

I need to move elements between 2 vector<unique_ptr> with some condition checkings. After moving, I will ignore the from-vector (transfer the ownership to the to-vector).

Case 1: Move from vector<unique_ptr<Derived>> fromDeriveds to vector<unique_ptr<Base>> toBases:

vector<unique_ptr<Derived>> fromDeriveds;
vector<unique_ptr<Base>> toBases;
   
for (unique_ptr<Derived> &derived: fromDeriveds)
{
    if (derived->prop == 1)
    {
        toBases.push_back(move(derived));
    }
}

This case is good.

Case 2: Move from vector<unique_ptr<Base>> fromBases to vector<unique_ptr<Derived>> toDeriveds:

vector<unique_ptr<Base>> fromBases;
vector<unique_ptr<Derived>> toDeriveds;
   
for (unique_ptr<Base> &base: fromBases)
{
    Derived *derived = dynamic_cast<Derived *>(base.get());
    if (derived && derived->prop == 1)
    {
        toDeriveds.push_back(move(base));
    }
}

It fails to compile with the following error:

main.cpp:44:44: error: no matching function for call to ‘std::vector<std::unique_ptr<Derived> >::push_back(std::remove_reference<std::unique_ptr<Base>&>::type)’
             toDeriveds.push_back(move(base));
                                            ^

Although I can do it by the following way, it is a copy instead of a move:

vector<unique_ptr<Base>> fromBases;
vector<unique_ptr<Derived>> toDeriveds;
   
for (unique_ptr<Base> &base: fromBases)
{
    Derived *derived = dynamic_cast<Derived *>(base.get());
    if (derived && derived->prop == 1)
    {
        toDeriveds.push_back(make_unique<Derived>(*derived));
    }
}

Is there any better way to achieve the goal?

1 Answers

You can iterate on fromBase list and make your checks, if that requirement is met then you can simply release the pointer - which gives away the base pointer without calling destructor on it. Just make sure to remove such empty pointers that were released, in case you need to use fromBase vector again.

WANDBOX LINK

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

using namespace std;

class Base{ public: virtual ~Base() = default; };
class Derived: public Base {};


int main() {
    vector<unique_ptr<Base>> fromBase;
    vector<unique_ptr<Derived>> toDerived;
    
    fromBase.emplace_back(make_unique<Base>());
    fromBase.emplace_back(make_unique<Base>());
    fromBase.emplace_back(make_unique<Derived>());
    
    for(auto& p: fromBase) {
        Derived* d = dynamic_cast<Derived *>(p.get());
        // make other checks here
        if(d != nullptr) {
            toDerived.emplace_back(d);
            p.release();
        }
    }

    // make sure to remove released pointers
    fromBase.erase(remove_if(fromBase.begin(), fromBase.end(), [](const auto& p) { return !p; }), fromBase.end());
    
    cout << fromBase.size() << " " << toDerived.size() << endl;
}
Related