3

I would like to create a deep copy of a class that contains a unique_ptr.

How can this be achieved?

I have the following example:

#include <iostream>
#include <unordered_map>
#include <string>
#include <memory>

class test;

typedef std::unordered_map<std::string, std::string> sMap;
typedef std::unordered_map<std::string, std::unique_ptr<test>> testMap;

class test {
    public:
    sMap     map1;
    testMap  map2; 

    test(){}

    // test(const test& t)
    // : 
    // map1(t.map1),
    // map2(t.map2) ??
    // {}
};

int main() {
    sMap myMap; 
    myMap["a"] = "b";

    std::unique_ptr<test> tobj= std::make_unique<test>();
    test& obj = *tobj;
    obj.map1 = myMap;

    test obj2;
    obj2.map2.emplace("one", std::move(tobj));

    // test obj3 (obj2); // error
    return 0;
}

How can I copy the data in the map2 when calling the copy constructor?

Kind regards

Trailer
  • 155
  • 9
  • 2
    What is the copy supposed to do with the unique_ptrs? – Mat Jan 09 '23 at 20:46
  • related/dupe: https://stackoverflow.com/questions/34797849/unique-copy-of-vectorunique-ptr – NathanOliver Jan 09 '23 at 20:53
  • seems a little bit off then having a unique_ptr in the class, better with a shared_ptr – AndersK Jan 09 '23 at 20:54
  • I was not able to compile the code with `std::unordered_map`. So I tried it with smart pointers and it worked. But I needed a way to copy the objects when the copy constructor or assignment operator got called – Trailer Jan 10 '23 at 16:23

1 Answers1

5

If you want to copy the instances that the unique_ptrs point at, you need to do so manually because unique_ptrs are non-copyable. No two unique_ptrs should ever point at the same instance!

Example:

class test {
public:
    sMap     map1;
    testMap  map2; 

    test() = default;

    test(const test& other) : map1(other.map1) { // copy constructor
        for(auto&[str, ptr] : other.map2) {
            if(ptr) // recursively copy construct:
                map2.emplace(str, std::make_unique<test>(*ptr));
            else // empty pointer, put `nullptr` in there:
                map2.emplace(str, nullptr);
        }
    }

    test(test&& other) noexcept = default;      // move constructor

    test& operator=(const test& other) {        // copy assignment
        // copy construct an instance and move assign it:
        *this = test(other);
        return *this;
    }

    test& operator=(test&& other) noexcept = default; // move assignment
};
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 1
    Small sidenote: In case there are classes that inherit from `test` within that map than this will result in [object slicing](https://en.wikipedia.org/wiki/Object_slicing) (in that case something like a virtual `clone()` method would be needed, e.g. [like in this answer](https://stackoverflow.com/a/43263477/8411406)) - if on the other hand there are no potential subclasses of test then the map could be implemented without `unique_ptr`'s (i.e. `std::unordered_map`), which would make `test` copyable by default. – Turtlefight Jan 10 '23 at 00:01
  • That strategy seems good. I would have to create a pure virtual function on `test` and than write a `clone` function. Thats it? but that way I will not be able to create `test` objects – Trailer Jan 10 '23 at 09:26
  • I will try to make it and post it here – Trailer Jan 10 '23 at 09:48
  • 1
    @Trailer If you _do_ use `unique_ptr` because of polymorphism and not one of the other reasons one may use it, adding a `virtual clone()` member is the idiomatic way to do it as Turlefight mentioned. Here's one way to start: [example](https://godbolt.org/z/z8G8b3W3T). I haven't tested it or put too much thought into it though, so take it with a grain of salt. – Ted Lyngmo Jan 10 '23 at 10:03
  • How can you copy a link from that web site? Edit: Got it!! I tried the implementation on the post. https://godbolt.org/z/nMfTa1jza but it won't compile :/ – Trailer Jan 10 '23 at 10:20
  • 1
    @Trailer With that approach, you need to change your `typedef`s: [example](https://godbolt.org/z/ffoTqcsWx) (see the `testMap` change I made) – Ted Lyngmo Jan 10 '23 at 10:58
  • @TedLyngmo, Sorry to bother you again with this topic. I tried this example https://godbolt.org/z/ErM3378r5. It compiles and seems to work. Would you be so kind as to verify the implementation? – Trailer Jan 11 '23 at 19:32
  • 1
    @Trailer I'd add all 5 of the "big 5" in the base class lsince it is polymorphic ([example](https://godbolt.org/z/Y1e4M68rv)) but other than that, it looks ok. Note that for code reviews, there's a better site: https://codereview.stackexchange.com/ – Ted Lyngmo Jan 12 '23 at 07:48