1

I have a class which contains a data member that occasionally needs to be recalculated, though it is likely to be accessed multiple times before a recalculation is needed. I have defined both const and non-const getter methods for the data, and within the getter method I would like to check whether or not the calculation needs to be run, and call the appropriate method if it does. In the non-const getter, I check whether the value is up-to-date, and if not run the calculation before returning the data. Doing the same in the const getter caused the compiler to complain about the this object being a const type while the calculation method is non-const. I was therefore surprised that the code compiles if I simply call the non-const getter within the const getter. Could someone please explain why this works, because I am slightly confused.

class A {

bool isUpToDate=false;
double someData=0;

// Do some calculation
void calcData() 
{
    someData = doSomeCalc();
    isUpToDate = true; // data is now up-to-date
}

// non-const getter
double& data() 
{
    if(!isUpToDate) 
    {
        // run the calculation only if data is not up-to-date
        calcData()
    }
    return someData;
}

// const getter that doesn't work
const double& data() const 
{
    if(!isUpToDate)
    {
        calcData() // compiler error: "this object is type const A but 
        calcData is non-const"
    }
    return someData;
}

// const getter that (mysteriously) works
const double& data() const 
{
    return data(); // why doesn't the compiler complain that data() is non-const?
}

I am sure that this behavior is actually reasonable and well-defined, I am just curious why it works because I don't understand what is happening here. I am using g++ as my compiler with the c++11 standard, in case that is a relevant factor in why this works.

4 Answers4

2

You're not calling the non-const getter; you're calling yourself:

const double& data() const
{
    return data();
}

is infinite recursion.

Because of data() const, *this is effectively const within the method. The call data() is equivalent to this->data(). Because this is a const A *, this selects the const getter again.

I was therefore surprised that the code compiles

Sure it compiles, but it doesn't actually work. It will either go into an infinite loop and hang or just crash.

melpomene
  • 84,125
  • 8
  • 85
  • 148
0

In this scenario, you can declare someData and isUpToDate as being mutable. That means they can be modified even from a const member function. Then make calcData() be const, so that it can be called from another const member function.

Such an approach will often be eyed with suspicion! I use it for mutexes and for caches. I worry that yours might be perhaps slightly too much for this.

Do you really need data() to be const? You should make sure…

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • So the problem is that there are places in the code where ```A``` itself is passed as a ```const``` argument to a function that uses ```A```'s data but doesn't modify it, but ```A``` is not ```const``` over its own lifetime. I suppose I could change the function that is using ```A```, but that is part of a third-party library that I would rather not touch. I think instead I will try to find another place to check ```isUpToDate``` and call the calculation methods and define my getters to just ```return someData;``` I just thought it would be nice if the getters could trigger the update. – Brandon Whitchurch Jul 25 '19 at 17:25
0

Make that field mutable, and use an update function:

bool updateNeeded; 
double getNewValue(); 

class MyResource {
    mutable double someData = 0.0;

    void update() const {
        // Because someData is mutable, this works fine
        if(updateNeeded()) {
            someData = getNewValue(); 
        }
    }
   public:
    double& getData() { 
        update(); 
        return someData;
    }
    double const& getData() const {
        update();
        return someData; 
    }
};
Alecto Irene Perez
  • 10,321
  • 23
  • 46
0

When you have a member function like const double& data() const, all functions called by data must also be const, and not modify the object. For those that stumble onto this: Meaning of 'const' last in a function declaration of a class?


There are some ways to solve this:

  1. You can change void calcData() to void calcData() const this will tell the compiler that the calculation method is const. This won't work here because your are assigning the result so it is non-const (it modifies member variables) so the compiler will yell at you.

  2. You can change double const& getData() const to double const& getData(). But this will not work if you need A to be const sometimes.

  3. Another solution is

    class A {
    
    bool isUpToDate=false;
    double someData=0;
    
    double& data() 
    {
        if(!isUpToDate) 
        {
            // run the calculation only if data is not up-to-date
            someData = doSomeCalc();
            isUpToDate = true;
        }
        return someData;
    }
    
    double data() const // Don't change anything, just return by value
    {
        if(!isUpToDate)
        {
            return doSomeCalc();
        }
        return someData;
    }
    }
    

This will return by value and re-calculate everything. The disadvantage is that it may be bad performance-wise if doSomeCalc() is intensive, since it needs to call it every time and cannot update someData.

  1. Other answers have already mentioned using the mutable qualifier for someData (and isUpToDate). This will work fine, but some may be averse to using the mutable keyword, as they are usually used for specific purposes and can be dangerous if handled poorly.
ChilliDoughnuts
  • 367
  • 2
  • 13