Search code examples
c++templatescopy-constructorunique-ptr

bool class template generic copy and move constructor


I was trying to implement a copy constructor for a class template that allows for all instantiations to converted from one version of the class template to the other.

This lead to the following code

#include <memory>
#include <iostream>

template<bool binary>
class Bar {
private:
    friend class Bar<!binary>;
    std::unique_ptr<int> data;
public:
    Bar(int example) : data(std::make_unique<int>(example)) {

    }

    template<bool value>
    Bar(const Bar<value>& bar) : data(std::make_unique<int>(*bar.data)) {

    }

    //other methods that differ depending on binary value...
};

int main() {
    Bar<false> b1{ 1 };
    Bar<false> b2{ b1 }; //causes compile error
    Bar<true> b3{ b1 }; //works as expected
}

Constructing from a different type works but constructing from the same type issues a compile time error stating

Bar::Bar(const Bar &)': attempting to reference a deleted function

Apparently the generic copy constructor isn't being called which led to me manually writing out the copy constructor, leading to the current code.

#include <memory>
#include <iostream>

template<bool binary>
class Bar {
private:
    friend class Bar<!binary>;
    std::unique_ptr<int> data;
public:
    Bar(int example) : data(std::make_unique<int>(example)) {

    }

    Bar(const Bar& bar) : data(std::make_unique<int>(*bar.data)) {

    }

    template<bool value>
    Bar(const Bar<value>& bar) : data(std::make_unique<int>(*bar.data)) {

    }

    //other methods that differ depending on binary value...
};

int main() {
    //all work as expected.
    Bar<false> b1{ 1 };
    Bar<false> b2{ b1 };
    Bar<true> b3{ b1 };
    Bar<true> b4{ b3 }; 
}

It's worth noting that this is also necessary for the move constructor, copy assignment operator, and move assignment operator. Thus, I have two questions

  1. Why doesn't the generic copy or move constructor overshadow the copy/move constructor?
  2. Is there a better workaround to allow for copy construction of both the same type and alternate type? As of right now I have to essentially duplicate code for each copy/move constructor and assignment operator.

Solution

  • From https://en.cppreference.com/w/cpp/language/copy_constructor

    A copy constructor of class T is a non-template constructor whose first parameter is T&‍, const T&‍, volatile T&‍, or const volatile T&‍, and either there are no other parameters, or the rest of the parameters all have default values.

    Hence you can't define new parameters for the copy ctor in that way.

    Now, if you use Bar<false>(const Bar<true>&) or Bar<false>(const Bar<true>&), the compiler will use the ordinary cpy ctor which has been generated on instantiating the class and won't instantiate your corresponding templated ctor.

    And If this is true, leave the job for the compiler to define the copy ctor but change the unique_ptr to shared_ptr(because with unique_ptr as a member var the class is not copyable), or define two ctors as you have in the second code.

    For the following code, I used shared_ptr and there is no error has occurred

    but notice that the templated cpy ctor doesn't execute for Bar<false/true>(const Bar<false/true>&) while it executes for Bar<true/false>(const Bar<false/false>&)

    #include <memory>
    #include <iostream>
    
    template<bool binary>
    class Bar {
    private:
        Bar& operator=(const Bar&) = delete ;
        friend class Bar<!binary>;
        std::shared_ptr<int> data;
    public:
    
        template<bool value>
        Bar(const Bar<value>& bar) : data(std::make_shared<int>(*bar.data)) {
            std::cout << __PRETTY_FUNCTION__ <<"\n";
    
        }
        Bar(int example) : data(std::make_shared<int>(example)) {
    
        }
    
        //other methods that differ depending on binary value...
    };
    
    int main() {
        Bar<false> b1{ 1 };
        Bar<false> b2{ b1 };
        Bar<true> b3 {b1};
        Bar<true> b4 {b3};
        Bar<false> b5 {b4};
    
    }
    

    Live