Home > Software engineering >  Changing type of template at run time
Changing type of template at run time

Time:05-07

I'm trying to make a Matrix struct which would work with various data types, including my Complex struct:

struct Complex {
    double re = 0, im = 0;

    Complex operator*(const Complex& other) const {
        return Complex(re * other.re - im * other.im, im * other.re   re * other.im);
    }
    Complex operator*(const double& other) const {
        return Complex(re * other, im * other);
    }

    Complex() {}
    Complex(double a) : re(a) {}
    Complex(double a, double b) : re(a), im(b) {}
};

std::ostream& operator<<(std::ostream& out, Complex z) {
    out << z.re << " " << z.im << "i";
    return out;
}

template <typename T>
Complex operator*(const T& c, const Complex& z) {
    return z * c;
}

The obvious way is to make a template like one in the code below:

template <typename T>
struct Matrix {
    std::vector<T> m;
    unsigned int rows, cols;

    Matrix<Complex> operator*(const Complex& z) const {
        Matrix<Complex> result(rows, cols);
        for (int i = 0; i < m.size(); i  ) {
            result.m[i] = m[i] * z;
        }
        return result;
    }

    void operator*=(const Complex& z) {
        (*this) = (*this) * z; // <- ideally we're trying to get this to work
    }
    void operator=(const Matrix<T>& other) {
        rows = other.rows;
        cols = other.cols;
        m.resize(rows * cols);
        m = other.m;
    }

    Matrix(const unsigned int& rows, const unsigned int& cols) : rows(rows), cols(cols) {
        m.resize(rows * cols);
    }
    Matrix(const Matrix<T>& other) : rows(other.rows), cols(other.cols) {
        (*this) = other;
    }
};

int main() {
    Complex z(1, 1);
    Matrix<double> A(1, 1);
    A.m[0] = 2.0;
    std::cout << (A * z).m[0] << std::endl; // works as it should because a temporary Matrix<Complex> gets created
    A *= z; // and here we're introducing the problem
    std::cout << A.m[0] << std::endl;
}

The problem arises when calling *= operator. We're trying to call an unexisting = operator overload. My first attempt was to write something like this instead:

template <typename T_other>
void operator=(const Matrix<T_other>& other) {
    rows = other.rows;
    cols = other.cols;
    m.resize(rows * cols);
    for (int i = 0; i < m.size(); i  ) {
        m[i] = other.m[i];
    }
}

This however leads to other problems:

  1. The type of A is still Matrix<double> and after the multiplication it should be Matrix<Complex> to store complex numbers.
  2. There is no conversion from Complex to double as it results in loss of data (the imaginary part).

Also, I would like to avoid creating a template specialization for Matrix<Complex>, but if there's no other way I'll accept it.

CodePudding user response:

C is statically typed. Once you declare a variable and type, you can't change the type of that variable.

template <typename T>
struct Matrix {
    void operator*=(const Complex& z) {
        (*this) = (*this) * z;
    }
}

The *= operator overload for your Matrix doesn't make sense. A Complex can hold the value of a double with imaginary part 0, but a double can never hold the value of a Complex.

Multiplying a real matrix by a complex number necessarily produces a complex matrix. So you try and assign the complex RHS to the real LHS - and either that makes no sense and shouldn't be done, or you have some idea for how to convert that (e.g. keep real part, keep absolute value etc) and then have to implement a Matrix<double> constructor from Matrix<Complex>.

Real numbers are a subset of complex numbers, so just make A a Matrix<Complex> from the beginning if you ever want to make it complex later.

  • Related