I'm continually irritated that matrix dimensions are accessed with getters. Sure one can have public rows and cols but these should be const
so that users of the Matrix class can't directly modify them. This is a natural way of coding but, because using const members is regarded, apparently incorrectly, as not possible without UB, results in people using non-const fields when they really should be consts that are mutable when needed such as resizing, or assigning.
What I'd like is something that can be used like this:
Matrix2D<double> a(2, 3);
int index{};
for (int i = 0; i < a.rows; i )
for (int ii = 0; ii < a.cols; ii )
a(i, ii) = index ;
but where a.rows=5;
isn't compilable because it's const. And it would be great if the class can be included in vectors and other containers.
Now comes: Implement C 20's P0784 (More constexpr containers) https://reviews.llvm.org/D68364?id=222943
It should be doable without casts or UB and can even be done when evaluating const expressions. I believe c 20 has made it doable by providing the functions std::destroy_at
and std::construct_at
. I have attached an answer using c 20 that appears to show it is indeed possible, and easily done, to provide const member objects in classes without an undue burden.
So the question is do the new constexpr functions in c 20, which provide the ability to destroy and then construct an object with different consts valid? It certainly appears so and it passes the consexpr lack of UB test.
CodePudding user response:
If you want to have const properties you can try this :
class Matrix {
public:
const float rows;
const float cols;
Matrix(float _rows, float _cols) : rows(_rows), cols(_cols)
{
}
};
The rows
and cols
properties of a Matrix
instance are initialized once in the constructor.
CodePudding user response:
Well, here's my pass at using const rows and cols in a 2D matrix. It passes UB tests and can be used in collections like vector w/o weird restrictions. It's a partial implementation. No bounds checking and such but to provide a possible approach to the problem.
matrix2d.h
#pragma once
#include <memory>
#include <vector>
template<class T>
class Matrix2D
{
public:
const size_t rows;
const size_t cols;
constexpr Matrix2D() : rows(0), cols(0){}
constexpr Matrix2D(size_t a_rows, size_t a_cols) : rows(a_rows), cols(a_cols), v(rows* cols) {}
constexpr Matrix2D(size_t a_rows, size_t a_cols, std::vector<T> a_v) : rows(a_rows), cols(a_cols), v(std::move(a_v)){}
constexpr Matrix2D(Matrix2D const& mat) : rows(mat.rows), cols(mat.cols)
{
v = mat.v;
}
constexpr Matrix2D(Matrix2D && mat) noexcept : rows(mat.rows), cols(mat.cols)
{
v = std::move(mat.v);
}
constexpr Matrix2D& operator=(Matrix2D const& mat)
{
std::destroy_at(this);
std::construct_at<Matrix2D, size_t, size_t, std::vector<T>>(this, 0 mat.rows, 0 mat.cols, std::vector<T>(mat.v));
return *this;
}
constexpr Matrix2D& operator=(Matrix2D&& mat) noexcept
{
std::destroy_at(this);
std::construct_at<Matrix2D, size_t, size_t, std::vector<T>>(this, 0 mat.rows, 0 mat.cols, std::vector<T>(std::move(mat.v)));
return *this;
}
constexpr T& operator()(size_t row, size_t col)
{
return v[row * cols col];
}
constexpr Matrix2D operator (Matrix2D const& v1)
{
auto ret = *this;
for (size_t i = 0; i < ret.v.size(); i )
ret.v[i] = v1.v[i];
return ret;
}
constexpr Matrix2D operator-(Matrix2D const& v1)
{
auto ret = *this;
for (size_t i = 0; i < ret.v.size(); i )
ret.v[i] -= v1.v[i];
return ret;
}
private:
std::vector<T> v;
};
Source.cpp
#include "matrix2d.h"
constexpr size_t foo()
{
Matrix2D<double> a(2, 3), b;
int index{};
for (int i = 0; i < a.rows; i )
for (int ii = 0; ii < a.cols; ii )
a(i, ii) = index ;
b = a a;
return b.rows b.cols;
}
// validating no UB in Matrix2D;
constexpr size_t x = foo(); // sets x=5
int main()
{
Matrix2D<double> a(2, 3), b;
int index{};
for (int i = 0; i < a.rows; i )
for (int ii = 0; ii < a.cols; ii )
a(i, ii) = index ;
b = Matrix2D<double>(10, 10);
b = a a-a;
std::vector<Matrix2D<double>> v;
v.push_back(a);
v.push_back(b);
}