我正在为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的用户知道包装器并且确实想要包装器的地址时,这也可能是反直观的。

除了抛出之外,您可以指定尝试修改对角线右侧的行为是不确定的。

10-05 18:01