【发布时间】:2019-09-03 07:21:04
【问题描述】:
我正在为下/上三角矩阵(doubles)编写一个类。通过利用n*n 三角矩阵只有n*(n + 1)/2 [可能非零] 元素这一事实,我在内部仅在平面数组成员中存储了该数量的元素。
首先,我有一个“正常”(即密集)矩阵的基类,operator() 作为下标运算符,它采用行索引和列索引:
class Matrix {
public:
// [...]
virtual const double &operator()(unsigned i, unsigned j);
virtual double &operator()(unsigned i, unsigned j);
// [...]
private:
std::valarray<double> data_;
std::size_t size_;
}
// [...]
const double &Matrix::operator()(unsigned i, unsigned j) {
return data_[size_*i + j];
}
对于三角矩阵(以后我以下三角矩阵为例),为了提供与正则矩阵相同的接口,我需要实现一个稍微不同的下标运算符:
const double &LowerTriangular::operator()(unsigned i, unsigned j) const override {
return data_[i*(i + 1)/2 + j];
}
上面的操作符并不完整,因为如果有人要求输入在对角线右侧(但仍在理论矩阵内)的条目,则会返回另一个(不相关的)元素,但应该返回 0 .
由于引用不能绑定到局部变量,我不能只是return 0。那么,我该如何实现呢?
我只能想出一个局部静态变量:
const double &LowerTriangular::operator()(unsigned i, unsigned j) const override {
static const double zero = 0;
if (j > i) return zero;
return data_[i*(i + 1)/2 + j];
}
我可以让函数按值返回,但是非常量版本(当调用者实际需要修改内容时)呢?如何确保调用者不修改 zero 静态变量?这可行,但有点难看:
const double &LowerTriangular::operator()(unsigned i, unsigned j) const override {
static double zero = 0;
if (j > i) return zero = 0; // kind of ugly but works
return data_[i*(i + 1)/2 + j];
}
double &LowerTriangular::operator()(unsigned i, unsigned j) override {
return const_cast<double &>( const_cast<const LowerTriangular &>(*this)(i, j) );
}
那么最好的解决方案是什么?
【问题讨论】:
-
如果有人请求超出范围的数据会被认为是例外吗?如果是这样,您可以抛出异常。
-
或者考虑使用
std::optional。 -
如果保证
operator()的结果可以有意义地赋值,那么j > i的情况确实是例外,因为它们不会兑现该保证。在这种情况下,正确的做法是抛出异常。如果不是这种情况,则用户负责验证结果是否有效,这开辟了许多可能性。最简单的可能是只返回一个指针,当j > i时使用nullptr。什么是正确的实际上取决于您打算如何使用您的类型。 -
@Anakhand 您可能需要考虑不返回
double &,而是返回一个代理类型,该代理类型的行为类似于对双精度的引用,但会进行坐标检查。一种可隐式转换为double并可从double分配的类型。编辑:然后您可以以任何您想要的方式处理对不受支持的坐标的分配。虽然我不清楚当有人试图分配一个不属于你三角形的元素时,你希望你的类型做什么。 -
我希望使用矩阵引用的函数(不知道矩阵是否为三角形)能够正常使用接口。我考虑这种设计目标在某种程度上也很危险。一个函数在上三角形中意外写入值(!= 0),这些值神奇地“消失”,因此读回提供的值与写入的值不同。我会睡得更好,除了对上三角形的写访问,对于写 0.0 的例外情况可能不会抛出。 (...来自异常的例外情况... ;-))
标签: c++ matrix reference containers