I'm creating a simple C++ matrix template class, with the following definition:
template<uint n, uint m, typename T = double>
class Matrix {
private:
T data[n][m];
static Matrix<n, m, T> I;
public:
Matrix();
Matrix(std::initializer_list<T> l);
T& at(uint i, uint j); // one-based index
T& at_(uint i, uint j); // zero-based index
template<uint k> Matrix<n, k, T> operator*(Matrix<m, k, T>& rhs);
Matrix<m, n, T> transpose();
Matrix<n, m, T> operator+(const Matrix<n, m, T>& rhs);
Matrix<n, m, T>& operator+=(const Matrix<n, m, T>& rhs);
Matrix<n, m, T> operator-(const Matrix<n, m, T>& rhs);
Matrix<n, m, T>& operator-=(const Matrix<n, m, T>& rhs);
Matrix<n, m, T> operator*(const T& rhs);
Matrix<n, m, T>& operator*=(const T& rhs);
Matrix<n, m, T> operator/(const T& rhs);
Matrix<n, m, T>& operator/=(const T& rhs);
static Matrix<n, m, T> identity();
};
(uint is defined as an unsigned int)
The final function Matrix<n, m, T> identity() aims to return the static I member which is the identity matrix using a basic singleton pattern. Obviously the identity matrix is only defined for square matrices so I tried this:
template<uint n, typename T>
inline Matrix<n, n, T> Matrix<n, n, T>::identity() {
if (!I) {
I = Matrix<n, n, T>();
for (uint i = 0; i < n; ++i) {
I.at(i, i) = 1;
}
}
return I;
}
Which gives the error C2244 'Matrix<n,n,T>::identity': unable to match function definition to an existing declaration.
My impression was that I could do some sort of specialisation of the template where the number of columns and rows are equal. I'm not sure if this is even possible, but your help would be much appreciated.
Try this:
static Matrix<n, m> identity() {
static_assert(n == m, "Only square matrices have a identity");
return {}; //TODO
}
See: http://cpp.sh/7te2z
Partial specialization of a template class is allowed for the entire class, but not for its single member.
Members of partial specializations are not related to the members of the primary
template.
That is the reason behind your compilation error.
Of course, specializing the entire Matrix template for the square matrix case doesn't make sense. #tkausl has already answered how to make the identity() function available only for the square matrix types.
However, I would like to draw your attention toward a couple of issues in your implementation of Matrix:
The matrix data is a plain array (instead of a dynamically allocated array or std::vector). This has the following disadvantages:
sizeof(Matrix<N, M, T>) == sizeof(T)*N*M. Allocating large matrices on the stack may result in stack overflow.
Impossibility of taking advantage of move semantics (as the data is inseparable from the matrix object).
Though, if the matrix dimensions are fixed at compile time constants, then, most probably, they will be small numbers. If so, paying the cost of dynamic allocation may indeed be unjustified.
identity() returns its result by value (rather than by constant reference).
I (the identity matrix) is a static member of the class. It is better to make it a static variable inside the identity() function.
Related
I have templated MxN matrix class
template<typename T, size_t M, size_t N>
class Matrix {
public:
Matrix() { vecs_ = new Vector<T, N>[M]; }
Matrix(std::initializer_list<std::initializer_list<T>> l);
Matrix(const Matrix& m);
~Matrix() { delete[] vecs_; }
Matrix& operator=(const Matrix& m);
Matrix& operator+=(const Matrix& m);
Matrix& operator-=(const Matrix& m);
Matrix& operator*=(T c);
Matrix& operator/=(T c);
Vector<T, N> operator[](int row) const;
Vector<T, N>& operator[](int row);
Vector<T, M> operator*(const Vector<T, N>& b) const;
template<size_t P> Matrix<T, M, P> operator*(const Matrix<T, N, P>& b) const;
Matrix<T, N, M> Transpose() const;
size_t Rows() const { return M; }
size_t Columns() const { return N; }
protected:
Vector<T, N>* vecs_;
};
template<typename T, size_t M, size_t N> Matrix<T, M, N> operator+(const Matrix<T, M, N>& m) { return m; }
template<typename T, size_t M, size_t N> Matrix<T, M, N> operator-(const Matrix<T, M, N>& m) { return Matrix<T, M, N>() - m; }
template<typename T, size_t M, size_t N> Matrix<T, M, N> operator+(const Matrix<T, M, N>& a, const Matrix<T, M, N>& b) { return a += b; }
template<typename T, size_t M, size_t N> Matrix<T, M, N> operator-(const Matrix<T, M, N>& a, const Matrix<T, M, N>& b) { return a -= b; }
template<typename T, typename U, size_t M, size_t N> Matrix<T, M, N> operator*(Matrix<T, M, N> m, U c) { return m *= c; }
template<typename T, typename U, size_t M, size_t N> Matrix<T, M, N> operator*(U c, Matrix<T, M, N> m) { return m *= c; }
template<typename T, typename U, size_t M, size_t N> Matrix<T, M, N> operator/(Matrix<T, M, N> m, U c) { return m /= c; }
template<typename T, size_t M, size_t N>
std::ostream& operator<<(std::ostream& os, const Matrix<T, M, N>& m) { ... }
I wish to create NxN square matrix class that is derived from MxN matrix. This child class should inherit everything the parent have, including its operator overloads. This is what I tried
template<typename T, size_t N>
class SquareMatrix : public Matrix<T, N, N>
{
public:
using Matrix<T, N, N>::Matrix; // inherit all constructors from the base class
using Matrix<T, N, N>::operator=; // also inherit other operators
using Matrix<T, N, N>::operator+=;
using Matrix<T, N, N>::operator-=;
using Matrix<T, N, N>::operator*=;
using Matrix<T, N, N>::operator/=;
using Matrix<T, N, N>::operator*;
SquareMatrix(bool identity);
};
Is it a proper way to inherit? Are there any redundant lines? Is there anything else I need to add?
Also, I have problems with * and [] operator. In the parent matrix class, I have two overloads for each of these operators, each takes different parameter. Is there any way to distinguish between these two in the child square matrix class? using Matrix<T, N, N>::operator*; is bit ambiguous to compile.
I imagine all your functions work with matrices of arbitary sizes right? If i manually declare 2 square matricies:
Matrix<int, 5, 5> m1, m2;
Do operations work on them? If so, then you don't need inheritance, you just need to make it easy for the user to make this specific version of the matrix type, which you can achieve with a type alias:
template<typename T, size_t N>
using SquareMatrix = Matrix<T, N, N>;
And everything should just work.
If you want to add functions that are specific to the square matrix, aim to make them free functions:
template <typename T, size_t N>
constexpr SquareMatrix<T, N> identity() noexcept {
...
}
This gives you a nice abstraction and is very modern c++ style. AKA, a matrix could easily be set as follows:
auto m = matrix::identity<int, 5>();
As above,but I define matrix dimension by template parameter.When I try to implement a matrix multiplication, some trouble occurs.My matrix multiplication prototype as bellow:
matrix_array<class __item__, std::size_t ROW, std::size_t COL>;
matrix_array<__item__, ROW, COL> operator* (
const matrix_array<__item__, COL, >& b) const;
So there is a question, how to pass the third template argument of matrix b?I can just to define a new template parameter, but it's so terrible.
In matrix multiplication, one "size" should be common between the 2 matrices, so some naming choice might be confusing.
With non-member function, it might be simpler to understand, your expected signature would be:
template <typename T, std::size_t ROW, size_t K, size_t COLUMN>
Matrix<T, ROW, COLUMN> operator * (const Matrix<T, ROW, K>& lhs,
const Matrix<T, K, COLUMN>& rhs);
So as member, lhs would be *this:
template <typename T, std::size_t ROW, size_t COL /*K*/>
class Matrix
{
// ...
template <size_t COLUMN>
Matrix<T, ROW, COLUMN> operator * (const Matrix<T, COL, COLUMN>& rhs) const;
// "confusing" name: rhs has 2 "column" names
};
I have written a matrix class which can take different sizes. Now I want to unroll loops for specific sizes. How should I do this?
The only way I can seem to get working is a child-class for 2-d. But this I would like to avoid, as it would result in much duplicate code.
For example:
#include <iostream>
template<class T, size_t M, size_t N>
class matrix
{
matrix<T,M,N>& operator*= (const matrix<T,M,N> &B);
};
template<class T, size_t M, size_t N>
matrix<T,M,N>& matrix<T,M,N>::operator*= (const matrix<T,M,N> &B)
{
// ...
return *this;
}
int main()
{
return 0;
}
Now I would like to add an implementation for the case that M = 2 and N = 2 where I unroll all loops to gain efficiency.
(I have timed the unroll in a previous implemenation, and it really does seem to make sense, in particular for more complicated operations then featured in this example.)
You can delegate operator*= to an overloaded function template. E.g.:
template<class T, size_t M, size_t N>
class matrix
{
public:
matrix<T,M,N>& operator*=(const matrix<T,M,N>&);
};
// Generic version.
template<class T, size_t M, size_t N>
void inplace_dot(matrix<T,M,N>& a, matrix<T,M,N> const& b);
// Overload for 2x2 matrix.
template<class T>
void inplace_dot(matrix<T,2,2>& a, matrix<T,2,2> const& b);
template<class T, size_t M, size_t N>
matrix<T,M,N>& matrix<T,M,N>::operator*=(const matrix<T,M,N>& b)
{
inplace_dot(*this, b);
return *this;
}
I wrote a generic matrix class like so:
template <std::size_t N, std::size_t M, class T>
class CMatrix
{
protected:
T* m_elem;
public:
CMatrix() : m_elem(new T[N*M]) {}
~CMatrix() { delete[] m_elem; }
inline bCMatrix& operator*=(const T& val);
inline bCMatrix& operator/=(const T& val);
...
template <std::size_t L> inline CMatrix<L, N, T> operator*(const CMatrix<M, L, T>& mat) const;
};
Now I would like to make several specializations for CMatrix<2,2,T>.
I'd like to add a comstructor CMatrix<2,2,T>(const T& a00, const T& a01, const T& a10, const T& a11) {...}.
And I like to specialize the matrix multiplication for CMatrix<2x2>*CMatrix<2x2>. But on the other hand I'd like to keep the functionalities of *= and /= and so on.
How would I do so?
My only working attempt is to rewrite (copy/past/search/replace) all the code for the specialization N=3, M=3, like:
template <class T>
class CMatrix<3,3,T>
{
...
};
Now I can specialize the matrix-matrix multiplication for 2x2. But I have two nearly identical pieces of code, like one for the generic matrix-vector multiplication and one for the matrix-vector multiplication with matrix 2x2. Which both have to be changed if I optimize the generic one. Sucks to maintain that.
You can read some documentation on class template, such as http://en.cppreference.com/w/cpp/language/partial_specialization. For you it entails defining the specialization in a way like this:
template<typename T>
class CMatrix<2, 2, T>
{
public:
CMatrix<2,2,T>(const T& a00, const T& a01, const T& a10, const T& a11);
...
template <std::size_t L> inline CMatrix<L, 2, T> operator*(const CMatrix<2, L, T>& mat) const;
// Special overload for 2x2 x 2x2 multiplication
inline CMatrix<2, 2, T> operator*(const CMatrix<2, 2, T>& mat) const;
};
To prevent duplication of code for operator*= etc you can make a template<int M, int N, typename T> class CMatrixBase {} and implement them in there, and have your actual matrix classes and specializations inherit from that.
Martin suggested to use CRTP, which helps you to not lose your type information. This would look like this:
template<int M, int N, typename T, typename Derived>
class CMatrixBase
{
public:
Derived& operator*=(T val) {
doMultiplication(val);
return *static_cast<Derived*>(this);
}
};
Consider the following code snippet for vector and matrix multiplication:
#include <array>
template<typename T,size_t N> using vec = std::array<T,N>;
template<typename T,size_t N,size_t M> using mat = vec<vec<T,M>,N>;
template<typename T,typename U,size_t N>
vec<T,N> operator*(const vec<T,N>& a,const vec<U,N>& b){
return {}; //implement componentwise mult.
}
template<typename T,typename U,size_t N,size_t M,size_t L>
mat<T,L,M> operator*(const mat<T,N,M>& a,const mat<U,L,N>& b){
return {}; //implement matrix mult.
}
int main(){
mat<float,4,4> a,b;
auto c = a * b;
}
I define 2 operator* overloads, where the second one explicitly uses matrices.
This will result in an "ambigous overload" error on GCC 5.2, although in my opinion the second overload is more specialized than the first one.
I now replace the typedefs in the second overload:
mat<T,L,M> operator*(const mat<T,N,M>& a,const mat<U,L,N>& b)
becomes:
mat<T,L,M> operator*(const vec<vec<T,M>,N>& a,const vec<vec<U,N>,L>& b)
Edit:
I made a small typo... thats why the resolution worked, I swapped N and L, so the type of b was const vec<vec<U,L>,N>& b.
Only with this typo the overload can be resolved normally.
My question is: Is this behavior welldefined by the standard or a bug?
With VS2015:
your code
template<typename T, typename U, size_t N>
vec<T, N> operator*(const vec<T, N>& a, const vec<U, N>& b) {
return{}; //implement componentwise mult.
}
template<typename T, typename U, size_t N, size_t M, size_t L>
mat<T, L, M> operator*(const mat<T, N, M>& a, const mat<U, L, N>& b) {
return{}; //implement matrix mult.
}
results in
Error C2593 'operator *' is ambiguous
Ambiguity derives from the fact that in vec<T, N> operator*(const vec<T, N>& a, const vec<U, N>& b) T can be interpreted as a vec<T,M>.
And if I replace them as you suggested with
template<typename T, typename U, size_t N>
vec<T, N> operator*(const std::array<T, N>& a, const std::array<U, N>& b) {
return{}; //implement componentwise mult.
}
template<typename T, typename U, size_t N, size_t M, size_t L>
mat<T, L, M> operator*(const vec<vec<T, M>, N>& a, const vec<vec<U, N>, L>& b) {
return{}; //implement matrix mult.
}
the results doesn't change.
Error C2593 'operator *' is ambiguous
Are you sure about your solution? Maybe you are not considering const?