C++ 如何从(通常)返回引用(成员数据)的成员函数返回常量

C++ 如何从(通常)返回引用(成员数据)的成员函数返回常量,c++,matrix,reference,containers,C++,Matrix,Reference,Containers,我正在为上下三角矩阵(double)编写一个类。通过利用n*n三角矩阵只有n*(n+1)/2[潜在非零]元素这一事实,在内部,我只在平面数组成员中存储该数量的元素 首先,我有一个“普通”(即稠密)矩阵的基类,其中操作符()作为下标操作符,它采用行索引和列索引: 类矩阵{ 公众: // [...] 虚常量double&运算符()(无符号i,无符号j); 虚拟双精度运算符(&O)((无符号i,无符号j); // [...] 私人: std::valarray数据; 标准:尺寸; } // [...]

我正在为上下三角矩阵(double)编写一个类。通过利用
n*n
三角矩阵只有
n*(n+1)/2
[潜在非零]元素这一事实,在内部,我只在平面数组成员中存储该数量的元素

首先,我有一个“普通”(即稠密)矩阵的基类,其中
操作符()
作为下标操作符,它采用行索引和列索引:

类矩阵{
公众:
// [...]
虚常量double&运算符()(无符号i,无符号j);
虚拟双精度运算符(&O)((无符号i,无符号j);
// [...]
私人:
std::valarray数据;
标准:尺寸;
}
// [...]
常量double&矩阵::运算符()(无符号i,无符号j){
返回数据[size_*i+j];
}
对于三角矩阵(我将以下三角矩阵为例),为了提供与常规矩阵相同的接口,我需要实现一个稍微不同的下标运算符:

constdouble&LowerTriangular::operator()(无符号i,无符号j)常量重写{
返回数据_ui*(i+1)/2+j];
}
但上面的运算符并不完整,因为如果有人要求输入对角线右侧(但仍在理论矩阵内)的条目,则会返回另一个(不相关)元素,但应返回
0

因为引用不能绑定到局部变量,所以我不能只
返回0
。因此,我怎样才能做到这一点

我只能想出一个局部静态变量:

constdouble&LowerTriangular::operator()(无符号i,无符号j)常量重写{
静态常数双零=0;
如果(j>i)返回零;
返回数据_ui*(i+1)/2+j];
}
我可以让函数按值返回,但非常量版本(当调用方实际需要修改内容时)如何?如何确保调用方不修改
zero
静态变量?这是可行的,但有点难看:

constdouble&LowerTriangular::operator()(无符号i,无符号j)常量重写{
静态双零=0;
如果(j>i)返回0=0;//有点难看,但可以工作
返回数据_ui*(i+1)/2+j];
}
double&LowerTriangular::operator()(无符号i,无符号j)重写{
返回const_cast(const_cast(*本)(i,j));
}

那么,最佳解决方案是什么?

当您通过常量引用返回时:


基本类型(您选择的优化与您提供的界面冲突

一种方法可能是不返回引用,而是返回一个行为类似于引用的透明包装器。类似于
std::vector:::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:::reference
具有相同的警告,即获取引用的地址不会给您指向double对象的指针。这可能是重载
运算符&
有意义的少数情况之一。但是,当API的用户知道包装器并在我想要包装纸的地址


而不是抛出,您可以指定尝试修改对角线的右侧是未定义的行为。如果有人请求边界之外的数据,那么将被认为是异常的吗?如果是这样,您可以抛出异常。或者考虑使用<代码> STD::可选< <代码> >如果您的代码>操作程序()的结果保证是有意义的可分配的,那么

j>i
的情况是真正例外的,因为它们不会遵守该保证。在这种情况下,正确的做法是抛出一个异常。如果不是这样,用户负责验证结果是否有效,这就打开了许多可能性。最简单的一个可能只是返回一个指针,用<代码> NulLPTR < /代码>当<代码> j> i>代码>。正确的取决于您打算如何使用您的类型。@ AANKHORD您可能会考虑不返回<代码> Bug和<代码>,而是返回一个代理类型,该代理类型作为引用的一个双倍,但它进行坐标检查。at可隐式转换为
double
,并可从
double
赋值。编辑:然后,您可以以任何方式处理对不受支持坐标的赋值。尽管我不清楚,当有人试图将类型赋值给不属于三角形的元素时,您希望类型执行什么操作。我希望函数在用矩阵引用(谁不知道矩阵是不是三角形)来正常使用接口。我认为这个设计目标也是危险的。函数在上三角形中写入意外值(=0),“消失”。神奇的是,回读提供了一个不同于写入的值。如果对上三角的写入访问有异常,我会睡得更好,因为写入0.0的异常情况可能不会抛出该异常。(…来自异常的异常情况…;-))对,我可能应该按常量版本的值返回。但对于非常量版本,挑战在于我强烈希望接口的行为与一般矩阵的行为完全相同事实上,这个类是另一个类的子类,
matrix
。我希望使用
Matrix
references(不知道矩阵是否为三角形)的函数能够正常使用接口,在访问对角线右侧的元素时不会引发异常(实际上并非“超出边界”).值得注意的是,这并不完全正确。虽然您无法直接为
运算符()(…)常量的结果赋值,但可以将结果复制到的非
常量
实例中