C++ 可变与惰性评估

C++ 可变与惰性评估,c++,constants,lazy-evaluation,mutable,C++,Constants,Lazy Evaluation,Mutable,最近我正在阅读有关常量正确性的常见问题。现在我遇到了下面的情况,我不知道如何使常量或可变。 假设一个简单的例子: class Averager { public: Averager() : sum(0),isUptoDate(false),N(0){} void add(double x){ sum+=x; N+=1; isUptoDate = false; } double getAverage() const {

最近我正在阅读有关常量正确性的常见问题。现在我遇到了下面的情况,我不知道如何使常量或可变。 假设一个简单的例子:

class Averager {
public:
    Averager() : sum(0),isUptoDate(false),N(0){}
    void add(double x){
        sum+=x;
        N+=1;
        isUptoDate = false;
    }
    double getAverage() const {
        if (!isUptoDate){updateAverage();}
        return average;
    }
private:
    void updateAverage(){
        if(N>0){average = sum / N;}
        else   {average = 0;}
        isUptoDate = true;
    }    
    double sum;
    mutable bool isUptoDate;
    int N;
    double average;
};
在实际情况中,
updateAverage()
是一个昂贵的计算,因此我希望避免每次添加值时都进行更新。另外,在添加新值之前,
getAverage()
可能会被多次调用,因此我只想在真正需要时进行更新。另一方面,调用
updateAverage()
不应该是类的用户的责任,因此我使用该标志来知道是否必须进行更新

据我所知,
getAverage()
显然应该是一个const方法,而
isupdate
可以是可变的(它不是逻辑状态的一部分,只是一个私有实现细节)。但是,
updateAverage()
绝对不是const,我不能在const方法中调用它


我的方法有什么问题?

在我看来很好,你只需要使你的
平均值也可变,因为它将由
getAverage
惰性地计算
updateAverage
也应该是
const
,因为它将被
getAverage
调用。由于
updateAverage
是私有的,因此它本身的存在是一个实现细节。它只被调用一次,您也可以将它内联到
getAverage

double getAverage() const {
    if (!isUptoDate){
        if(N>0){average = sum / N;}
        else   {average = 0;}
        isUptoDate = true;
    }
    return average;
}
事实上,我确实建议您将其内联,因为将其放在头文件中没有任何意义(如果更改其签名或常量,则必须重新编译所有用户)。如果在实际情况中不只是3行,则可以将其作为lambda,如果使用C++11:

double getAverage() const {
    auto updateAverage=[&]{
        if(N>0){average = sum / N;}
        else   {average = 0;}
        isUptoDate = true;
    };
    if (!isUptoDate){ updateAverage(); }
    return average;
}

事实上,我把这当作一个解决办法,我只是想编辑这个问题来提一下。但不知何故,我觉得这样做是不好的(或“邪恶的”)。谢谢你给出了很好的理由,为什么它真的没问题。另外,要注意数值问题。懒散的评估会带来重大问题,因为你通常会在同一个尺度上把数字加在一个越来越大的总和上。考虑使用补偿算法:注意,类违背线程安全默认假设(const成员是安全同时使用),因为<代码> GealGeals写了一些东西。这很好,但记录下来确实是个好主意。或者,您可以使其线程安全。@SebastianRedl是否在某处声明了此默认假设?@GiulioFranco我同意。但是我仍然认为线程安全性的缺乏应该被记录下来。