Does the 'mutable' keyword have any purpos

2019-01-01 01:07发布

问题:

A while ago I came across some code that marked a member variable of a class with the mutable keyword. As far as I can see it simply allows you to modify a variable in a const method:

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

Is this the only use of this keyword or is there more to it than meets the eye? I have since used this technique in a class, marking a boost::mutex as mutable allowing const functions to lock it for thread-safety reasons, but, to be honest, it feels like a bit of a hack.

回答1:

It allows the differentiation of bitwise const and logical const. Logical const is when an object doesn\'t change in a way that is visible through the public interface, like your locking example. Another example would be a class that computes a value the first time it is requested, and caches the result.

Since c++11 mutable can be used on a lambda to denote that things captured by value are modifiable (they aren\'t by default):

int x = 0;
auto f1 = [=]() mutable {x = 42;};  // OK
auto f2 = [=]()         {x = 42;};  // Error: a by-value capture cannot be modified in a non-mutable lambda


回答2:

The mutable keyword is a way to pierce the const veil you drape over your objects. If you have a const reference or pointer to an object, you cannot modify that object in any way except when and how it is marked mutable.

With your const reference or pointer you are constrained to:

  • only read access for any visible data members
  • permission to call only methods that are marked as const.

The mutable exception makes it so you can now write or set data members that are marked mutable. That\'s the only externally visible difference.

Internally those const methods that are visible to you can also write to data members that are marked mutable. Essentially the const veil is pierced comprehensively. It is completely up to the API designer to ensure that mutable doesn\'t destroy the const concept and is only used in useful special cases. The mutable keyword helps because it clearly marks data members that are subject to these special cases.

In practice you can use const obsessively throughout your codebase (you essentially want to \"infect\" your codebase with the const \"disease\"). In this world pointers and references are const with very few exceptions, yielding code that is easier to reason about and understand. For a interesting digression look up \"referential transparency\".

Without the mutable keyword you will eventually be forced to use const_cast to handle the various useful special cases it allows (caching, ref counting, debug data, etc.). Unfortunately const_cast is significantly more destructive than mutable because it forces the API client to destroy the const protection of the objects (s)he is using. Additionally it causes widespread const destruction: const_casting a const pointer or reference allows unfettered write and method calling access to visible members. In contrast mutable requires the API designer to exercise fine grained control over the const exceptions, and usually these exceptions are hidden in const methods operating on private data.

(N.B. I refer to to data and method visibility a few times. I\'m talking about members marked as public vs. private or protected which is a totally different type of object protection discussed here.)



回答3:

Your use with boost::mutex is exactly what this keyword is intended for. Another use is for internal result caching to speed access.

Basically, \'mutable\' applies to any class attribute that does not affect the externally visible state of the object.

In the sample code in your question, mutable might be inappropriate if the value of done_ affects external state, it depends on what is in the ...; part.



回答4:

Mutable is for marking specific attribute as modifiable from within const methods. That is its only purpose. Think carefully before using it, because your code will probably be cleaner and more readable if you change the design rather than use mutable.

http://www.highprogrammer.com/alan/rants/mutable.html

So if the above madness isn\'t what mutable is for, what is it for? Here\'s the subtle case: mutable is for the case where an object is logically constant, but in practice needs to change. These cases are few and far between, but they exist.

Examples the author gives include caching and temporary debugging variables.



回答5:

It\'s useful in situations where you have hidden internal state such as a cache. For example:

class HashTable
{
...
public:
    string lookup(string key) const
    {
        if(key == lastKey)
            return lastValue;

        string value = lookupInternal(key);

        lastKey = key;
        lastValue = value;

        return value;
    }

private:
    mutable string lastKey, lastValue;
};

And then you can have a const HashTable object still use its lookup() method, which modifies the internal cache.



回答6:

Well, yeah, that\'s what it does. I use it for members that are modified by methods that do not logically change the state of a class - for instance, to speed up lookups by implementing a cache:

class CIniWrapper
{
public:
   CIniWrapper(LPCTSTR szIniFile);

   // non-const: logically modifies the state of the object
   void SetValue(LPCTSTR szName, LPCTSTR szValue);

   // const: does not logically change the object
   LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;

   // ...

private:
   // cache, avoids going to disk when a named value is retrieved multiple times
   // does not logically change the public interface, so declared mutable
   // so that it can be used by the const GetValue() method
   mutable std::map<string, string> m_mapNameToValue;
};

Now, you must use this with care - concurrency issues are a big concern, as a caller might assume that they are thread safe if only using const methods. And of course, modifying mutable data shouldn\'t change the behavior of the object in any significant fashion, something that could be violated by the example i gave if, for instance, it was expected that changes written to disk would be immediately visible to the app.



回答7:

mutable does exist as you infer to allow one to modify data in an otherwise constant function.

The intent is that you might have a function that \"does nothing\" to the internal state of the object, and so you mark the function const, but you might really need to modify some of the objects state in ways that don\'t affect its correct functionality.

The keyword may act as a hint to the compiler -- a theoretical compiler could place a constant object (such as a global) in memory that was marked read-only. The presence of mutable hints that this should not be done.

Here are some valid reasons to declare and use mutable data:

  • Thread safety. Declaring a mutable boost::mutex is perfectly reasonable.
  • Statistics. Counting the number of calls to a function, given some or all of its arguments.
  • Memoization. Computing some expensive answer, and then storing it for future reference rather than recomputing it again.


回答8:

Mutable is used when you have a variable inside the class that is only used within that class to signal things like for example a mutex or a lock. This variable does not change the behaviour of the class, but is necessary in order to implement thread safety of the class itself. Thus if without \"mutable\", you would not be able to have \"const\" functions because this variable will need to be changed in all functions that are available to the outside world. Therefore, mutable was introduced in order to make a member variable writable even by a const function.

The mutable specified informs both the compiler and the reader that it is safe and expected that a member variable may be modified within a const member function.



回答9:

mutable is mainly used on an implementation detail of the class. The user of the class doesn\'t need to know about it, therefore method\'s he thinks \"should\" be const can be. Your example of having a mutex be mutable is a good canonical example.



回答10:

Your use of it isn\'t a hack, though like many things in C++, mutable can be hack for a lazy programmer who doesn\'t want to go all the way back and mark something that shouldn\'t be const as non-const.



回答11:

Use \"mutable\" when for things that are LOGICALLY stateless to the user (and thus should have \"const\" getters in the public class\' APIs) but are NOT stateless in the underlying IMPLEMENTATION (the code in your .cpp).

The cases I use it most frequently are lazy initialization of state-less \"plain old data\" members. Namely, it is ideal in the narrow cases when such members are expensive to either build (processor) or carry around (memory) and many users of the object will never ask for them. In that situation you want lazy construction on the back end for performance, since 90% of the objects built will never need to build them at all, yet you still need to present the correct stateless API for public consumption.



回答12:

In some cases (like poorly designed iterators), the class needs to keep a count or some other incidental value, that doesn\'t really affect the major \"state\" of the class. This is most often where I see mutable used. Without mutable, you\'d be forced to sacrifice the entire const-ness of your design.

It feels like a hack most of the time to me as well. Useful in a very very few situations.



回答13:

The classic example (as mentioned in other answers) and the only situation I have seen the mutable keyword used in so far, is for caching the result of a complicated Get method, where the cache is implemented as a data member of the class and not as a static variable in the method (for reasons of sharing between several functions or plain cleanliness).

In general, the alternatives to using the mutable keyword are usually a static variable in the method or the const_cast trick.

Another detailed explanation is in here.



回答14:

The mutable keyword is very useful when creating stubs for class test purposes. You can stub a const function and still be able to increase (mutable) counters or whatever test functionality you have added to your stub. This keeps the interface of the stubbed class intact.



回答15:

Mutable changes the meaning of const from bitwise const to logical const for the class.

This means that classes with mutable members are longer be bitwise const and will no longer appear in read-only sections of the executable.

Furthermore, it modifies type-checking by allowing const member functions to change mutable members without using const_cast.

class Logical {
    mutable int var;

public:
    Logical(): var(0) {}
    void set(int x) const { var = x; }
};

class Bitwise {
    int var;

public:
    Bitwise(): var(0) {}
    void set(int x) const {
        const_cast<Bitwise*>(this)->var = x;
    }
};

const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.

int main(void)
{
    logical.set(5); // Well defined.
    bitwise.set(5); // Undefined.
}

See the other answers for more details but I wanted to highlight that it isn\'t merely for type-saftey and that it affects the compiled result.



回答16:

The mutable can be handy when you are overriding a const virtual function and want to modify your child class member variable in that function. In most of the cases you would not want to alter the interface of the base class, so you have to use mutable member variable of your own.



回答17:

The very keyword \'mutable\' is actually a reserved keyword.often it is used to vary the value of constant variable.If you want to have multiple values of a constsnt,use the keyword mutable.

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   


回答18:

One of the best example where we use mutable is, in deep copy. in copy constructor we send const &obj as argument. So the new object created will be of constant type. If we want to change (mostly we won\'t change, in rare case we may change) the members in this newly created const object we need to declare it as mutable.

mutable storage class can be used only on non static non const data member of a class. Mutable data member of a class can be modified even if it\'s part of an object which is declared as const.

class Test
{
public:
    Test(): x(1), y(1) {};
    mutable int x;
    int y;
};

int main()
{
    const Test object;
    object.x = 123;
    //object.y = 123;
    /* 
    * The above line if uncommented, will create compilation error.
    */   

    cout<< \"X:\"<< object.x << \", Y:\" << object.y;
    return 0;
}

Output:-
X:123, Y:1

In the above example, we are able to change the value of member variable x though it\'s part of an object which is declared as const. This is because the variable x is declared as mutable. But if you try to modify the value of member variable y, compiler will throw an error.