我正在为double
的较低/较高的三角形矩阵编写类。通过利用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];
}
对于三角矩阵(以下我将以较低的三角矩阵为例),为了提供与常规矩阵相同的接口(interface),我需要实现稍微不同的下标运算符:
const double &LowerTriangular::operator()(unsigned i, unsigned j) const override {
return data_[i*(i + 1)/2 + j];
}
但是,上面的运算符并不完整,因为如果有人要求输入对角线右边(但仍在理论矩阵内)的条目,则会返回另一个(不相关的)元素,但应返回
0
。由于引用不能绑定(bind)到局部变量,所以我不能只是
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];
}
我可以使函数按值返回,但是非const版本呢(当调用者实际上需要修改内容时)?如何确保调用者没有修改
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::vector::<bool>::reference
。一个例子:
struct Reference {
double* element;
operator double() const {
return element
? *element
: 0;
}
Reference& operator=(double d) {
if (!element)
throw std::out_of_range("Cannot modify right side of diagnoal");
*element = d;
return *this;
}
};
const Reference
LowerTriangular::operator()(unsigned i, unsigned j) const {
return {
j > i
? nullptr
: data_ + i*(i + 1)/2 + j
};
}
Reference
LowerTriangular::operator()(unsigned i, unsigned j) {
return {
j > i
? nullptr
: data_ + i*(i + 1)/2 + j
};
}
这确实与
std::vector::<bool>::reference
有相同的警告,即获取引用的地址不会给您指向double对象的指针。这可能是重载operator&
有意义的少数情况之一。但是,当API的用户知道包装器并且确实想要包装器的地址时,这也可能是反直观的。除了抛出之外,您可以指定尝试修改对角线右侧的行为是不确定的。