我建议使用一维数组,如user2079303 suggested in their answer。但是,如果这不是可取的,也可以使用模板或多态来实现您的想法。
如果您不需要动态更改其维度,您可以将矩阵设为模板类,使用其维度作为模板参数。这反过来又允许将维度用于 SFINAE 的编译时逻辑,从而允许您根据矩阵的维度重载 operator[]()。
请注意,这实际上在Rows == 1 或Cols == 1 时不会创建一维数组,它只是模拟一个;如果您明确需要该行为,而不是技术上不同但大部分等效的行为,您将需要研究一个或两个参数为1 时的专业化类。这可能会涉及复制至少一些代码,所以会有点混乱。
// This example uses std::array for the actual array, since it's compile-time anyways.
// This, in turn, lets it declare a lot of functions constexpr.
// Const-correctness omitted for brevity. Remember to restore it for actual code.
template<size_t Rows, size_t Cols>
class Matrix {
std::array<std::array<double, Cols>, Rows> mat;
public:
// Default constructor. Clang _really_ likes braced initialiser lists.
constexpr Matrix() : mat{{{{0}}}} {}
// Array constructor.
constexpr Matrix(const decltype(mat)& arr) : mat(arr) {}
// -----
// Subscript operators.
// Generic operator. Matrix<x, y>, where x != 1 && y != 1.
// Does what normal subscript operators do.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<!R && !C, size_t> i) {
return mat[i];
}
// Magic operator. Matrix<1, x>, where x != 1.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<(R && !C), size_t> i) {
return mat[0][i];
}
// Magic operator. Matrix<x, 1>, where x != 1.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<C && !R, size_t> i) {
return mat[i][0];
}
// Scalar matrix operator. Matrix<1, 1>.
// Just returns mat[0][0], for simplicity's sake. Might want to make it do something
// more complex in your actual class.
template<bool R = (Rows == 1), bool C = (Cols == 1)>
auto& operator[](std::enable_if_t<R && C, size_t> i) {
return mat[0][0];
}
// -----
// A few interface helpers.
// Simple begin() & end(), for example's sake.
// A better version would begin at mat[0][0] and end at mat[Rows - 1][Cols - 1].
constexpr auto begin() const { return mat.begin(); }
constexpr auto end() const { return mat.end(); }
// Generic helpers.
constexpr size_t size() const { return mat.size() * mat[0].size(); }
constexpr size_t rows() const { return mat.size(); }
constexpr size_t cols() const { return mat[0].size(); }
// 1D Matrix helpers.
constexpr bool is_one_d() const { return (Rows == 1) || (Cols == 1); }
constexpr bool one_row() const { return Rows == 1; }
constexpr size_t dimension() const { return (one_row() ? cols() : rows()); }
// -----
// Output.
// Would need modification if better begin() & end() are implemented.
friend std::ostream& operator<<(std::ostream& str, const Matrix<Rows, Cols>& m) {
for (auto& row : m) {
for (auto& elem : row) {
str << std::setw(6) << elem << ' ';
}
str << '\n';
}
str << std::endl;
return str;
}
};
它可以用作...
// Get rid of any "Waah, you didn't use that!" warnings.
// See https://stackoverflow.com/a/31654792/5386374
#define UNUSED(x) [&x]{}()
// This should really use if constexpr, but online compilers don't really support it yet.
// Instead, have an SFINAE dummy.
template<size_t Rows, size_t Cols>
void fill(Matrix<Rows, Cols>& m, std::enable_if_t<(Rows == 1) || (Cols == 1), int> dummy = 0) {
UNUSED(dummy);
//for (size_t i = 0; i < (m.one_row() ? m.cols() : m.rows()); i++) {
for (size_t i = 0; i < m.dimension(); i++) {
m[i] = (i ? i : 0.5) * (i ? i : 0.5);
}
}
template<size_t Rows, size_t Cols>
void fill(Matrix<Rows, Cols>& m, std::enable_if_t<!((Rows == 1) || (Cols == 1)), int> dummy = 0) {
UNUSED(dummy);
for (size_t i = 0; i < m.rows(); i++) {
for (size_t j = 0; j < m.cols(); j++) {
m[i][j] = (i ? i : 0.5) * (j ? j : 0.5) + (i >= j ? 0.1 : -0.2);
}
}
}
看到它在行动here。
如果您确实需要能够即时更改其尺寸,这将变得更加复杂。最简单的解决方案可能是使用多态性,并让operator[] 返回一个代理。
class Matrix {
protected:
// Out proxy class.
// Visible to children, for implementing.
struct SubscriptProxy {
virtual operator double&() = 0;
virtual operator double*() = 0;
virtual double& operator=(double) = 0;
virtual double& operator[](size_t) = 0;
virtual ~SubscriptProxy() = default;
};
public:
virtual SubscriptProxy& operator[](size_t i) = 0;
virtual ~Matrix() = default;
virtual void out(std::ostream& str) const = 0;
friend std::ostream& operator<<(std::ostream& str, const Matrix& m) {
m.out(str);
return str;
}
};
std::ostream& operator<<(std::ostream& str, const Matrix& m);
然后您可以让每个类创建它需要的代理成员public,以及不需要的代理成员private; private 得到了一个非功能性的虚拟实现。
// Resizing omitted for brevity.
class OneDMatrix : public Matrix {
double arr[5];
// Proxy for single element.
class OneDProxy : public SubscriptProxy {
double& elem;
operator double*() override { return &elem; }
double& operator[](size_t) override { return elem; }
public:
OneDProxy(double& e) : elem(e) {}
operator double&() override { return elem; }
double& operator=(double d) override {
elem = d;
return elem;
}
};
public:
OneDMatrix() : arr{0} {}
// operator[] maintains a static pointer, to keep the return value alive and guarantee
// proper cleanup.
SubscriptProxy& operator[](size_t i) override {
static OneDProxy* ret = nullptr;
if (ret) { delete ret; }
ret = new OneDProxy(arr[i]);
return *ret;
}
void out(std::ostream& str) const override {
for (size_t i = 0; i < 5; i++) {
str << std::setw(4) << arr[i] << ' ';
}
str << std::endl;
}
};
// Resizing omitted for brevity.
class TwoDMatrix : public Matrix {
double arr[3][4];
// Proxy for array.
class TwoDProxy : public SubscriptProxy {
double* elem;
operator double&() override { return elem[0]; }
double& operator=(double) override { return elem[0]; }
public:
TwoDProxy(double* e) : elem(e) {}
operator double*() override { return elem; }
double& operator[](size_t i) override { return elem[i]; }
};
public:
TwoDMatrix() : arr{{0}} {}
// operator[] maintains a static pointer, to keep the return value alive and guarantee
// proper cleanup.
SubscriptProxy& operator[](size_t i) override {
static TwoDProxy* ret = nullptr;
if (ret) { delete ret; }
ret = new TwoDProxy(arr[i]);
return *ret;
}
void out(std::ostream& str) const override {
for (size_t i = 0; i < 3; i++) {
for (size_t j = 0; j < 4; j++) {
str << std::setw(4) << arr[i][j] << ' ';
}
str << '\n';
}
}
};
由于引用可用于多态性,Matrix 可以用作接口,允许任何不特别需要实际类之一的东西采用Matrix&。
看到它在行动here。
通过这样的设置,通过提供辅助函数,动态创建正确类型的矩阵变得很容易。
// Assume OneDMatrix is expanded for dynamic size specification.
// It now has constructor OneDMatrix(size_t sz), and owns a dynamic double[sz].
// Assume TwoDMatrix is expanded for dynamic size specification.
// It now has constructor TwoDMatrix(size_t r, size_t c), and owns a dynamic double[r][c].
Matrix* createMatrix(size_t rows, size_t cols) {
if (rows == 1) { return new OneDMatrix(cols); }
else if (cols == 1) { return new OneDMatrix(rows); }
else { return new TwoDMatrix(rows, cols); }
}
像往常一样,模板版本更冗长,但更安全(因为不必使用指针)并且可能更有效(因为不需要做那么多的动态分配和释放;我还没有测试过这个,但是)。