I use Boost-Operatators to build a matrix class. (Toy project). However, I run into problems when I want to mix matrices of different types of elements.
Basically, I have a template class Matrix<T>, where Tis the type of the element of this matrix. I use Boost-Operators to define operators between instances Matrix<T>(for example, adding elements) between Matrix<T>and T(for example, scalar multiplication) and, if possible, also between Matrix<T>and Matrix<U>(for example, a real matrix plus a complex matrix).
The boost statements support one or two template arguments. One if you want operators to be between two objects of the same type, and two if you need mixed operators.
template<typename T>
class Matrix : boost::addable<Matrix<T>>
boost::multiplyable2<Matrix<T>,T>
However, I cannot give it Matrix<U>as a second argument, because then my class would have two template arguments, and the type would depend on which matrices I can work with.
template<typename T, typename U>
class Matrix : boost::addable2<Matrix<T,U>,Matrix<U,?>>
I also tried to implement my own version boost::addable, but that didn't work either. The compiler complains about the incomplete type.
template<class Derived>
class Addable {
template<class Other>
friend Derived operator+(Derived lhs, const Other &rhs) {
return lhs += rhs;
}
template<class Other>
friend Derived operator+(const Other &lhs, Derived rhs) {
return rhs += lhs;
}
};
Another approach was to define a cast constructor from Matrix<U>to Matrix<T>. However, now I have a problem, that these are two different types, and I do not get access to private members. So I need to do more public material than I want, or find another way to do this.
?
#include <cassert>
#include <utility>
#include <complex>
#include <vector>
#include <algorithm>
#include <iostream>
#include <boost/operators.hpp>
typedef double Real;
typedef std::complex<Real> Complex;
template<typename T>
class Matrix : boost::addable<Matrix<T>>
{
public:
Matrix() = default;
template<typename U>
Matrix(const Matrix<U> &other)
: m_(other.m()), n_(other.n()),
data_(other.data_.begin(), other.data_.end()) { }
Matrix(size_t m, size_t n) : m_(m), n_(n), data_(m*n) { }
Matrix(size_t m, size_t n, const T &initial)
: m_(m), n_(n), data_(m*n, initial) { }
size_t m() const { return m_; }
size_t n() const { return n_; }
size_t size() const {
assert(m_*n_ == data_.size());
return data_.size();
}
const T &operator()(size_t i, size_t j) const { return data_[i*m_ + j]; }
T &operator()(size_t i, size_t j) { return data_[i*m_ + j]; }
void fill(const T &value) {
std::fill(data_.begin(), data_.end(), value);
}
Matrix &operator+=(const Matrix &other) {
assert(dim_match(other));
for (int i = 0; i < size(); ++i) {
data_[i] += other.data_[i];
}
return *this;
}
friend std::ostream &operator<<(std::ostream &o, const Matrix &m) {
if (m.size() == 0) {
o << "()" << std::endl;
return o;
}
for (int i = 0; i < m.m(); ++i) {
o << "( ";
for (int j = 0; j < m.n() - 1; ++j) {
o << m(i,j) << ", ";
}
o << m(i, m.n() - 1) << " )" << std::endl;
}
return o;
}
private:
bool dim_match(const Matrix &other) {
return n_ == other.n_ && m_ == other.m_;
}
private:
int m_, n_;
typedef std::vector<T> Store;
Store data_;
};
int main() {
Matrix<Real> A(2,3, 1.);
Matrix<Complex> B(2,3, Complex(0,1));
auto C = Matrix<Complex>(A) + B;
std::cout << A << std::endl;
std::cout << B << std::endl;
std::cout << C << std::endl;
}