Simply put, because there are two sides to the destination: left and right.
Non-envelope version: T& operator() (unsigned i, unsigned j); intended primarily for the left side of the assignment (that is, for use as a destination)
Version const: T const& operator() (unsigned i, unsigned j) const; Designed exclusively for the right side of the assignment.
Note the difference in the wording there: the const version can be used only on the right side of the destination, while the version without a constant can be used on both sides. If, however, you have a const -qualified object, you can only call functions defined with const, so you cannot use it at all in this case. This is exactly what you (at least usually) want - it prevents the modification of the object you said should not be changed (using const-qualifying it).
As for mutate , it is usually used only for objects that have some difference between their logical state and their bit-muddy state. A common example is a class that does some (usually expensive) calculations lazily. To avoid recalculating the value, it saves the value after calculating it:
class numbers { std::vector<double> values; mutable double expensive_value; bool valid; public: numbers() : valid(false) {} double expensive_computation() const { if (valid) return expensive_value;
So, the result from expensive_computation depends only on the values ββin values . If you don't need speed, you can simply recalculate the value each time the user is called expensive_computation . Calling it repeatedly in a const object will always give the same result, although, therefore, by calling it once, we assume that it can be called again, and in order not to repeat the same expensive calculation repeatedly, we simply store the value in expensive_value . Then, if the user requests it again, we simply return the value.
In other words, from a logical point of view, the object remains const , even if / if we change the expensive_value . The visible state of the object does not change. All we did was let him complete const tasks faster.
For this to work correctly, we would also like to set valid back to false anytime the user changes the contents of values . For instance:
void numbers::add_value(double new_val) { values.push_back(new_val); valid = false; }
In some cases, we may also need an intermediate level of certainty - we could calculate an expensive_value faster (for example), knowing exactly which numbers were added to the values , and not just having a logical value to say whether it is valid or not.
I must add that C ++ 11 adds some new requirements to both const and mutable . In short, in most cases you need to ensure that everything that is const and / or mutable also thread safe. You can watch the Herb Sutter video. However, I feel obligated to add that, in my opinion, his conclusion is relatively mutable , probably a little exaggerated (but I would rather you watch and decide on your own than a word for that).