12

I have a class object which contains a vector<unique_ptr>. I want a copy of this object to run non-const functions on. The original copy must remain const.

What would the copy constructor for such a class look like?

class Foo{
public:
 Foo(const Foo& other): ??? {}

 std::vector<std::unique_ptr> ptrs;
};
Willy Goat
  • 1,175
  • 2
  • 9
  • 24
  • "Unique" here does not mean "extraordinary". – molbdnilo Jan 14 '16 at 19:21
  • @molbdnilo Surely you can copy the data pointed to by a unique_ptr. `data = *ptr;` – Willy Goat Jan 14 '16 at 19:23
  • Unique means just the one. Copying something unique is a contradiction in terms. I suspect you need `std::shared_ptr` if you want copies. – Galik Jan 14 '16 at 19:23
  • @WillyGoat yes, but that's not what you're trying to accomplish. That would leave you with two distinct but identical things. You're trying to create two vectors that both contain the same unique things, which is like putting one egg in two baskets. – molbdnilo Jan 14 '16 at 19:29
  • @molbdnilo: How do you know what he's trying to accomplish? – Benjamin Lindley Jan 14 '16 at 19:36

2 Answers2

22

You cannot simply copy a std::vector<std::unique_ptr> because std::unique_ptr is not copyable so it will delete the vector copy constructor.

If you do not change the type stored in the vector then you could make a "copy" by creating a whole new vector like

std::vector<std::unique_ptr<some_type>> from; // this has the data to copy
std::vector<std::unique_ptr<some_type>> to;
to.reserve(from.size()) // preallocate the space we need so push_back doesn't have to

for (const auto& e : from)
    to.push_back(std::make_unique<some_type>(*e));

Now to is a separate copy of from and can be changed independently.


Additionally: If your type is polymorphic the above won't work as you would have a pointer to the base class. What you would have to do is make a virtual clone member function and have clone return a std::unique_ptr to a copy of the actual derived object. That would make the code look like:

std::vector<std::unique_ptr<some_type>> from; // this has the data to copy
std::vector<std::unique_ptr<some_type>> to;
to.reserve(from.size()) // preallocate the space we need so push_back doesn't have to

for (const auto& e : from)
    to.push_back(e->clone());
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • This is what I want. Thank you – Willy Goat Jan 14 '16 at 19:26
  • 1
    @brian Thanks for catching the missing reference – NathanOliver Jan 14 '16 at 19:39
  • 1
    What happens when the vectors are destroyed, and all the unique_ptrs try to delete the objects they point to? – Bo Persson Jan 14 '16 at 21:34
  • @BoPersson It is a separate instance so the original vector would be left unchanged. – NathanOliver Jan 14 '16 at 21:58
  • @NathanOliver Instead of clone function can we call the right copy constructor using switch on `typeid(some_type)` ? – Matthieu H Jan 03 '20 at 18:16
  • 2
    @MatthieuH It's possible. It's a design flaw normally though. – NathanOliver Jan 03 '20 at 18:21
  • @NathanOliver So clone function is a best practice ? – Matthieu H Jan 03 '20 at 18:23
  • @m Yes. Otherwise you have a "maintenance point". i.e., if you add more derived types you need to visit this spot and add more if statements. If you use virtual functions, it just works. – NathanOliver Jan 03 '20 at 18:24
  • @NathanOliver And can we use dynamic_cast to call the right copy constructor? – Matthieu H Jan 03 '20 at 18:33
  • @MatthieuH You can, but again you are in the same place where you manually have to code all copy operation paths like you would with `typeid`. `typeid` and `dynamic_cast` are both going to have a run time hit so why not just go all out and use a virtual function? – NathanOliver Jan 03 '20 at 18:35
  • @NathanOliver Ok, I will do that but I searched a other solution because my class is complexe and I use default copy constructor. There I have to reimplement copy and if I forget a variable the copy will be wrong. – Matthieu H Jan 03 '20 at 18:45
  • 1
    @MatthieuH The `clone` method will use your default copy constructor. The whole function would just be something like `Derived * clone() { return new Derived(*this); }`. There is nothing to mess up with that. – NathanOliver Jan 03 '20 at 18:50
  • @NathanOliver Oh ! Thanks a lot. – Matthieu H Jan 03 '20 at 18:53
1

Nathan's answer above is great. Additionally in the the polymorphic case, I found this site useful for defining the clone() member function.

I used

std::unique_ptr<Base> Base::clone()
{
  return std::make_unique<Derived>(*this);
}

which is similar to Nathan's later comment.