Search code examples
c++classtemplatestype-conversion

Type conversion in user defined class in c++


I made a matrix class in c++

namespace LinAlg {
template <typename T> class Matrix {
public:
  Matrix();
  Matrix(const uint64_t &rows, const uint64_t &cols);
  template <typename P> operator P() const;

private:
  uint64_t rows_, cols_;
  std::vector<std::vector<T>> arr_;
 
};

template <typename T> template <typename P> Matrix<T>::operator P() const {
  if constexpr (std::is_same_v<P, Matrix<int>> ||
                std::is_same_v<P, Matrix<double>> ||
                std::is_same_v<P, Matrix<float>>) {
    Matrix<P> m(this->rows_, this->cols_);
    for (uint64_t i = 0; i < this->rows_; i++) {
      for (uint64_t j = 0; j < this->cols_; j++) {
        m.arr_[i][j] = static_cast<P>(this->arr_[i][j]);
      }
    }
    return m;
  }
  throw std::invalid_argument("Not a valid type conversions\n");
}
} // namespace LinAlg

I implemented the type-conversion for Matrix<T> into Matrix<P>. When I tried to convert Matrix<int> to Matrix<float> in my test.cpp using the following code:

  LinAlg::Matrix<int> M(3, 3);
  std::cin >> M;
  LinAlg::Matrix<float> M1(3, 3);
  std::cin >> M1;
  std::cout << static_cast<LinAlg::Matrix<float>>(M) + M1 << "\n";

I get errors like arr_ is private in the context:

m.arr_[i][j] = static_cast<P>(this->arr_[i][j]);

Solution

  • Your main problem is that Matrix<T1> and Matrix<T2> (for types T1,T2) are different and unrelated types.

    Therefore Matrix<T1> cannot access a private member in Matrix<T2>, and this is the source of the error you get about arr_ being private in this line:

    m.arr_[i][j] = static_cast<P>(this->arr_[i][j]);
    

    You can overcome this problem in 2 ways:

    1. Add public accessors (setters, getters) to Matrix, and use them in the implementation of operator P() instead of accessing the private arr_.
      This seems to me like the best solution because the users of the class will probably need accessor methods anyway to access the data.

    2. If you have good reasons to avoid adding accessors, and since you seem to support only Matrix<int>,Matrix<double>, Matrix<float> (based on your if constexpt), then you can make these 3 classes friends:

      friend Matrix<int>;
      friend Matrix<double>;
      friend Matrix<float>;
      

      This will enable you to access the private arr_.


    Another issue in your code is that P is the type of the whole Matrix<X> (for some type X), and not the type of the element in the Matrix.
    Therefore these 2 lines are wrong:

    Matrix<P> m(this->rows_, this->cols_);
    

    And:

    m.arr_[i][j] = static_cast<P>(this->arr_[i][j]);
    

    Because they both assume P is the type of the elements in the Matrix.

    To solve it you can add a type alias in Matrix (making it public can be useful):

    template <typename T> class Matrix {
    public:
        using element_type = T;
    
        // ...
    };
    

    Then use it in the 2 lines above:

    Matrix<P::element_type> m(...);
    

    And:

    ... = static_cast<P::element_type>(...);
    

    Note: you might need to use typename P::element_type instead of P::element_type (if your C++ version is earlier than C++20).