Home > Software engineering >  How to define a single copy constructor for template classes?
How to define a single copy constructor for template classes?

Time:12-16

#include <iostream>

template <typename T>
class Matrix
{

public:

    Matrix() = default;

    template <typename U>
    Matrix(const Matrix<U>& matrix) {
        std::cout << "Copying internal data..." << std::endl;
    }

//    Matrix(const Matrix<T>& matrix) {
//        std::cout << "Copying internal data..." << std::endl;
//    }

    Matrix(Matrix<T>&& matrix) {
        std::cout << "Moving internal data..." << std::endl;
    }
};

int main() {
    Matrix<int> m1{};
    Matrix<double> m2 = m1;
    Matrix<int> m3 = m1;
}

Here, I have a matrix class, it can be a matrix of int, a double, or any numerical value.

I want to define a copy constructor that accepts a matrix with any numerical type and copies its elements.

For example, suppose m1 is a Matrix<double> = {1.1, 2.2, 3.3, ...}, Matrix<int> m2 = m1 should set m2 to be {1, 2, 3, ...}.

Also, I want to have a move constructor, but it doesn't make any sense to have a move constructor for any type except for its own type (in this example, it's T).

This is because I'm going to steal the pointer pointing to the array of numbers, and to do so, it has to be of the same type.

Defining a move constructor that accepts only Matrix<T> automatically deletes the copy constructor for Matrix<T>.

I realized that since the parameter in the copy constructor I tried to make isn't necessarily of the same type, it's not considered to be a copy constructor, and unless I write a copy constructor specifically for Matrix<T> (the commented copy constructor), the code won't compile.

But even if I don't have a copy constructor, I have a constructor that accepts a matrix of any type. Why is it looking specifically for the copy constructor?

How do I define my copy constructor only once, and have it deal with matrices of any type?

CodePudding user response:

But even if I don't have a copy constructor, I have a constructor that accepts a matrix of any type. Why is it looking specifically for the copy constructor?

In overload resolution it is not find the best usable function. Instead, it is find the best function and try to use it. If it can't because of access restrictions or being deleted, then you get a compiler error.

In your case, you have the template constructor that stamps out Matrix(const Matrix<double>& matrix) (#1), and it also finds Matrix(const Matrix<double>& matrix) = delete (#2) which was implicitly generated by the compiler because you have a user provided move constructor. In overload resolution, if two functions have the exact same signature, and one of those is a specialization of a template, the non template version is chosen. In this case that is #2 which is deleted, so you get an error for accessing a deleted function.

So to answer

How do I define my copy constructor only once, and have it deal with matrices of any type?

You can't. If you want Foo<T> to be copyable from another Foo<T> then you need to provide a copy constructor. If you want Foo<T> to be copyable from a Foo<U>, then you need to add a converting constructor for that. Ideally you would use something like a std::vector as the underlying storage for the matrix elements, and if you do, then you can just follow the rule of zero and your class becomes

template <typename T>
class Matrix
{

public:

    Matrix() = default;

    template <typename U>
    Matrix(const Matrix<U>& other) data(other.data.begin(), other.data.end()) {}

private:
    std::vector<T> data;
};
  • Related