Correct alternative to a 'mutable function' in c++

Tom picture Tom · Nov 22, 2010 · Viewed 16k times · Source

I often have a problem with const correctness when wrapping algorithms in classes in c++. I feel that I want a mutable function, although this is not allowed. Can anyone advise me how to implement classes such as the one that follows?

The following is the code that I want to write.

  • The function run() should not be a const function because it changes the data.
  • The function get_result() should be a constant function (as far as the user is concerned) because it returns the data.

However, if the user requests the result without calling run(), I want the get_result() function to run the algorithm. This breaks the const correctness because I have a const function calling a non-const function.

class operate_on_data
{
  std::vector<double> m_data;  // the data to modify
  bool m_completed;  // check to see if the function run() has been called
public:
  operate_on_data(std::vector<double> data)
    : m_data(data), m_completed(false) {}  //initialise
  void run() //I don't want this function to be const  
  {
    //The algorithm goes here - it alters m_data.
    m_completed = true;  //the algorithm has been run
  }
  std::vector<double> get_result() const //I want this function to be const
  {
    /*The following breaks const correctness because 
      I am calling a non-const function from a const function
      - but this feels like the right thing to do ... */ 
    if (!m_completed) run();  //run the algorithm if it has not run already
    return m_data; //return
  }
};

The only way I have been I have managed to compile the above class is to either

  • make run() const, and make m_data and m_completed mutable. This works but is conceptually wrong because run() demonstrably changes data.
  • make get_result() not a constant function. This seems wrong too, for the user would expect this function to be a simple return, and therefore constant.
  • Put the run() function into the get_result() const function and make the data variables mutable.

My understanding was that the mutable keyword was designed for the third of these options, where the implementation requires data to be changed but the user reasonably expects a simple return and therefore const function.

However, I don't want to do this final option because I want the user to be able to choose exactly when they change the data. There is a chance that they will forget to call run(), however, and so I want to force the algorithm if they request the result without calling run(). I feel like a want to make run() mutable - but I'm not allowed to.

What is the correct way to write such a class?

Answer

Edward Strange picture Edward Strange · Nov 22, 2010

make run() const, and make m_data and m_completed mutable. This works but is conceptually wrong because run() demonstrably changes data.

Not true, actually. The variables within your class are, in fact, altered but you could never, ever demonstrate this. Calling run() does not change anything that the user is able to retrieve from your class's interface. If you can't retrieve any information about such a change then you can't possibly demonstrate that change. This is more than a semantic issue, it speaks directly to the whole point of the 'mutable' keyword.

The 'mutable' keyword is greatly misunderstood.

That said, though with the minimally given information I have I might do it the above way, I'm not recommending it. There's almost certainly a better method that would be apparent given a larger view of your problem.

The other method I might use is what you're apparently set on avoiding: force the user to call run() before using get_data(). To tell the truth though, this is a really suboptimal method too. Perhaps more so.

Edit:

If you do decide to use the mutable method then I'd suggest some changes. Having a function called 'run()' that is const and returns nothing of interest would be quite confusing. This function should certainly be non-const. Thus, what I would do, given a decision to do it this way already, is have run() call a const and private function that has the behavior of the current 'run()' function, which is also referred to by get_data() under the specified conditions.