c++copydeep-copyvariant

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


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.


Solution

  • 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.