1

I have a std::vector<std::unique_ptr<BaseType>>. Is there any clean way to do a deep copy of the vector?

The only thing that I can think of is to have a function that uses dynamic_cast to retrieve the derived type, and then copy that into a new object held by a unique_ptr. Given that I have control of all possible derived classes, this would be feasible. This has all kinds of obvious shortcomings.

Previously I had used a single class that was a union of all derived types. In attempting to get away from this I have encountered the situation of needing to copy the vector.

Is there any good solution to this problem? The only solutions that I can come up with are hideously ugly and cause me great shame to even consider. This is one largish step in attempting to refactor/cleanup code that I work with.


The vector is a member of a class that must be copyable. So, in this case, I just need to ensure that I can write a copy constructor for the containing class.

Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55
Graznarak
  • 3,626
  • 4
  • 28
  • 47
  • 8
    One solution is to implement the clone pattern, where `BaseType` has a `virtual std::unique_ptr clone() = 0;` function. Each derived type must implement it to return a copy of themselves. – François Andrieux Sep 18 '18 at 21:44
  • how do you make a deep copy of a `std::unique_ptr` ? Do the same n times, then you know how to copy a vector – 463035818_is_not_an_ai Sep 18 '18 at 21:44
  • 1
    @user463035818 It's not necessarily easy to deep copy a `std::unique_ptr` if `BaseType` is polymorphic. – François Andrieux Sep 18 '18 at 21:45
  • *Is there any clean way to do a deep copy of the vector?* Do you want to do that everytime a `vector` is copied, or do you want to use an explicitly defined function for the purpose? – R Sahu Sep 18 '18 at 21:45
  • @FrançoisAndrieux yes, but it is definitly not harder if the `std::unique_ptr` is inside a vector ;) – 463035818_is_not_an_ai Sep 18 '18 at 21:45
  • @RSahu The only place where the vector needs to be copied is mentioned in the latest edit as being when the enclosing class is copied. – Graznarak Sep 18 '18 at 21:50
  • 1
    @Graznarak, you certainly can do that. Remember to follow [The Rule of Three](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). – R Sahu Sep 18 '18 at 21:52
  • @RSahu Or the Rule of Five. In my case I could get away with the copy constructor, but I would definitely implement the copy assignment operator. Given that the `unique_ptr`s handle resources, I think that I don't need to implement the destructor or the moves. – Graznarak Sep 18 '18 at 21:55
  • @Graznarak, yeah. That should work. – R Sahu Sep 18 '18 at 21:56
  • In addition to the clone pattern, I would probably go with implementing a wrapper around `unique_ptr` which implements the copy constructor and copy assignment (using the polymorphic `clone()` method), then instantiate `vector` with that wrapper class. For now you might only need to do the copy in one place, but what about the future? – Daniel Schepler Sep 18 '18 at 22:46

1 Answers1

2

The easiest way is to implement some form of cloning, then using std::transform:

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

struct base {
    // For constructors and destructors, it's business as usual.
    // Remember to implement the rule of five.
    base() {std::cout << "new base\n";}
    base(const base& o) {
        std::cout << "copied base\n";
    }
    virtual ~base() {std::cout << "destructed base\n";}
    // This is the virtual copy function. We need this because only
    // the actual derived class will know how to copy itself. The
    // only way to forward this knowledge to a pointer to the base 
    // class is via a virtual function.
    // You can make this pure virtual, if you don't mind
    // the base being abstract (or if you *want* to make it 
    // abstract). It'll be safer this way.
    virtual base* copy() {return new base(*this);}
};

struct derived : base {
    derived() : base() {std::cout << "new derived";}
    derived(const derived& o) : base(o) {
        std::cout << "copied derived\n";
    }
    virtual ~derived() {std::cout << "destructed derived\n";}
    base* copy() override {return new derived(*this);}
};

// example of deep copying
int main() {
    std::vector<std::unique_ptr<base>> v;
    v.emplace_back(new base());
    v.emplace_back(new derived());
    std::vector<std::unique_ptr<base>> copy_of_v;

     // The transformation merely calls copy(). Each object will be copied into
     // an instance of the correct type. 
    std::transform(v.begin(), v.end(), std::back_inserter(copy_of_v), [](auto& item){
        return std::unique_ptr<base>(item->copy());
    });
}
Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55