How do you deep copy a variant of pointers in C++?

72 views Asked by At

I have a std::variant whose alternatives are all pointers.

class a {
  public:
    int* placeholder; 
    a(a& _copy) { /* Does a deep copy */ }
} 

class b {
  // For demonstrational purposes, identical to a.
}

int main() {
    std::vector<std::variant<a*, b*>> instances = // initialise vector, guaranteed no nullptrs. 
}

Now, for instance, if I want to push a deep copy of the first element to the back, I cannot simply do instances.push_back(instances[0]), because that does a shallow copy of the vector.

Of course, the obvious solution would be to check for all alternatives, then get_if, create a copy, and return the copy. However, I think there has to be a better way. How should something as such be done?

I am also open to solutions that do not use variants.

1

There are 1 answers

2
463035818_is_not_an_ai On BEST ANSWER

As mentioned in comments, you can use std::visit. I strongly suggest to use smart pointers.

#include <variant>
#include <memory>
#include <iostream>

int main() {
    // init and print x    
    std::variant<std::unique_ptr<int>,std::unique_ptr<std::string>> x = std::make_unique<std::string>("foo"); 
    auto print = [](const auto& x) { std::cout << *x << "\n";};
    std::visit(print,x);

    // make the copy
    auto deep_copy = [](const auto& x) -> std::variant<std::unique_ptr<int>,std::unique_ptr<std::string>> {
         
        return std::make_unique<std::remove_reference_t<decltype(*x)>>(*x); 
        // replace this creating a proper clone 
    };
    auto y = std::visit(deep_copy,x);    
    std::visit(print,y);
    
    // check that it actually is a deep copy
    auto modify = [](auto& x) { *x += 'a';};
    std::visit(modify,x);
    std::visit(print,x);
    std::visit(print,y);
}

Output:

foo
foo
fooa
foo

I'll leave it to you to modify the code in case each variant requires a different way to copy. The above is the simple case where all types can be handled by a templated lambda. For the case of actually requiring distinct branches for each variant I refer you to documentation (eg https://en.cppreference.com/w/cpp/utility/variant/visit). Most importantly (as pointed out by user17732522 in their comment), this std::make_unique<std::remove_reference_t<decltype(*x)>>(*x) is the wrong way to copy a std::unique_ptr<Base> managing a Derived instance.