简体   繁体   English

如何从成员函数(通常)返回常量(返回到成员数据)返回常量

[英]How to return a constant from a member function that (usally) returns a reference (to member data)

I'm writing a classes for lower/upper triangular matrices (of double s). 我正在为较低/较高的三角形矩阵( double s)编写一个类。 By taking advantage of the fact that an n*n triangular matrix only has n*(n + 1)/2 [potentially nonzero] elements, internally I'm storing only that amount of elements, in a flat array member. 通过利用n*n三角矩阵仅具有n*(n + 1)/2 [可能非零]元素的事实,在内部,我仅将一定数量的元素存储在平面数组成员中。

First of all, I have a base class for "normal" (ie dense) matrices, with operator() as a subscript operator that takes the row index and the column index: 首先,我有一个“普通”(即密集)矩阵的基类,其中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];
}

For triangular matrices (I'll take lower triangular matrices henceforth as an example), in order to provide the same interface as for a regular matrix, I need to implement a slightly different subscript operator: 对于三角矩阵(以下我将以较低的三角矩阵为例),为了提供与常规矩阵相同的接口,我需要实现稍微不同的下标运算符:

const double &LowerTriangular::operator()(unsigned i, unsigned j) const override {
    return data_[i*(i + 1)/2 + j];
}

The operator above is not complete though, since if someone asks for entries that are to the right of the diagonal (but still inside the theoretical matrix), another (unrelated) element is returned, but 0 should be returned instead. 上面的运算符并不完整,因为如果有人要求输入对角线右边(但仍在理论矩阵内)的条目,则会返回另一个(不相关的)元素,但应返回0

Since references mustn't bind to local variables, I can't just return 0 . 由于引用不能绑定到局部变量,所以我不能只return 0 Thus, how can I achieve this? 因此,我该如何实现呢?

I was only able to come up with making a local static variable: 我只能提出一个局部静态变量:

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];
}

I could make the function return by value, but what about the non-const version (when the caller actually needs to modify the contents)? 我可以使函数按值返回,但是非const版本呢(当调用者实际上需要修改内容时)? How can I make sure that the caller doesn't modify the zero static variable? 如何确保调用者没有修改zero静态变量? This works, but it's a bit ugly: 这有效,但是有点丑陋:

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) );
}

So what is the best solution? 那么最好的解决方案是什么?

Your chosen optimisation is in conflict with the interface that you provide. 您选择的优化与您提供的界面冲突。

One approach might be to not return a reference, but a transparent wrapper that behaves like a reference. 一种方法可能是不返回引用,而是行为类似于引用的透明包装器。 Something similar to std::vector::<bool>::reference . 类似于std::vector::<bool>::reference An example: 一个例子:

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
    };
}

This does have same caveats as std::vector::<bool>::reference , ie taking address of the reference doesn't give you pointer to the double object. 这与std::vector::<bool>::reference确实有相同的警告,即获取引用的地址不会给您指向double对象的指针。 This could be one of the few cases where overloading operator& makes sense. 这可能是少数重载operator&有意义的情况之一。 But that can be counter intuitive as well, when the user of the API is aware of the wrapper and does indeed want address of the wrapper. 但是,当API的用户知道包装器并且确实需要包装器的地址时,这也可能是反直观的。

Instead of throwing, you could specify that attempting to modify right side of diagonal is undefined behaviour. 除了抛出以外,您可以指定尝试修改对角线右侧的行为是不确定的。

When you return by constant reference: 通过常量引用返回时:

Basic types (<= 8 or even 16 bytes) are usually returned by value in such cases (read only). 在这种情况下,通常通过值返回基本类型(<= 8甚至16个字节)(只读)。 Consider returning double instead of const double& . 考虑返回double而不是const double&

When you return by non constant reference: 当您通过非常量引用返回时:

You can check the boundaries and throw an exception if accessing out of bounds or do as in many standard functions such as std::vector::operator[]: if accessing out of bounds, then this is undefined behaviour. 您可以检查边界并在超出范围时抛出异常,或者像在std :: vector :: operator []这样的许多标准函数中一样进行操作:如果超出范围,则这是未定义的行为。 Just document it so that the user of your function knows he must check himself. 只需对其进行记录,以便您的函数的用户知道他必须检查自己。

Note that overloads can have different return types so you can return by value in the const case and by reference in the non const case. 请注意,重载可以具有不同的返回类型,因此在const情况下可以按值返回,在非const情况下可以通过引用返回。

You could also store a "zero" in a field of you class and return a reference to it if accessing in the null half of the matrix. 您也可以在类的字段中存储“零”,如果访问矩阵的空半部分,则返回对其的引用。 You would have to set it to zero before returning the reference, just in case a malicious user changes it in the meantime. 在返回引用之前,您必须将其设置为零,以防万一恶意用户在此期间对其进行了更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM