2

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?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
kzfid
  • 688
  • 3
  • 10
  • 17
  • 3
    Related to [alternatives-of-static-pointer-cast-for-unique-ptr](https://stackoverflow.com/questions/36120424/alternatives-of-static-pointer-cast-for-unique-ptr) – Jarod42 Aug 04 '21 at 12:48

1 Answers1

1

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;
}
Abhinav Gauniyal
  • 7,034
  • 7
  • 50
  • 93
  • 2
    Slightly more concise is just use `remove(fromBase.begin(), fromBase.end(), nullptr)` inside the erase call. – Dave S Aug 04 '21 at 14:12
  • 1
    In C++20, you can use `std::erase(fromBase, nullptr);` – Remy Lebeau Aug 04 '21 at 17:04
  • Thanks all for the suggestion. As I do not care about the fromBase after picking up the record and emplacing to toDerived (so can just leave the fromBase out of scope). I change it to "Derived* d = dynamic_cast(p.release());", which also releases the ownership before having a new unique_ptr (https://wandbox.org/permlink/s0yfBPweNQKv3Fz5). But one point I am not quite is that you call "toDerived.emplace_back(d)" before "p.release();". Will this result in unexpected behavior as there are 2 unique_ptr to the same object? (https://wandbox.org/permlink/BaHW7dBhScUIkqYM) – kzfid Aug 05 '21 at 02:01
  • @kzfid no that shouldn't be undefined behavior, however, I'd still suggest keeping the code a bit cleaner as there's no harm in it. Also if you think my answer helped, you can mark it as well :) – Abhinav Gauniyal Aug 05 '21 at 09:25
  • It absolutely helps. ^_^ – kzfid Aug 05 '21 at 13:06