Stupid const, stupid mutable, stupid C++

2007-03-21

Every now and then someone drags me into a C++ discussion. I have friends who code in C++. Some of them do it for money. I’m sorry about that.

Recently I heard about “const methods”. “What’s that?” I ask; “Methods that are contractually obliged not to change their object.”. Oh, I think. That seems really useful, but a feature like that would never make into C++ (I reason, based on what I know of C). Still, not knowing much about C++ I shut up and go and learn some more C++ instead. I consult my friends who actually know more C++ than I do. The replies are not consistent, not confident, and sometimes go all slippery and vague when I press for details.

Then, it dawns on me. A “const method” is simply a method whose this pointer is const qualified. It’s an inevitable feature of the language once you’ve add both const and this; otherwise how would you invoke a method on a const instance, and what would the type of this be? You have to have methods with const this otherwise you end up throwing away constness and I’m sure we can all agree on what a bad idea that is. (of course when I say things like const this I mean that this has type const foo * (pointer to const thingy)).

Here’s the most important thing to know about const methods:

They cannot help the compiler; they cannot help you.

(Actually they can help you, if certain conventions are obeyed. The key thing is that the conventions are just that, so const methods cannot be relied upon to help you; there is no contractual obligation). There’s a lot of confusion about const on the web, particular about what it means for a method to be const. You see stuff like this (from this random webpage):

void inspect() const;   // This member promises NOT to change *this

This is trying to claim that declaring the method to be const means that the method promises not to change the object. This is rot. Aside from naughty const-removing casts that would allow the inspect method to modify the object, the inspect method might simply call a function that happens to use a global pointer (that is not const) to modify the object in question. To illustrate (if my C++ is not idiomatic, that’s because I don’t get paid to program in it; if it’s not correct, I want to know):

#include <iostream>

class counter {
  public:
  int i;

  counter();

  int inspect() const;
  void increment();
};

counter sigma_inspect;

counter::counter()
{
  i = 0;
}

int counter::inspect() const
{
  sigma_inspect.increment();
  return i;
}

void counter::increment()
{
  ++ i;
  return;
}

int main(void)
{
  counter a;

  std::cout << a.inspect() << "\\n";

  std::cout << sigma_inspect.inspect() << "\\n";
  std::cout << sigma_inspect.inspect() << "\\n";
  return 0;
}

inspect is a const method and increment is a non-const method. increment increments the value of a counter, and inspect returns the value of that counter. The inspect method also increments a global counter that records the total number of times that inspect has been called. Consider the two calls to sigma_inspect.inspect in the main function. They will print out as 2 and 3. The value of sigma_inspect.inspect() has changed even though inspect is a const method. This is of course because the object on which we are invoking the inspect method is sigma_inspect, the same object that inspect is using to keep track of the number of times it has been invoked. The reason that this works is that the inspect method is not using its this pointer to modify the sigma_inspect object, it is using the sigma_inspect global directly. It would be an error for the inspect method to try and modify *this because that’s a const lvalue and can’t be used to modify an object. Essentially the sigma_inspect object has been aliased.

The best that can be said for const methods is that they’re a useful convention. It is useful to denote in an interface which methods do not modify an object and which do, and const is a way of doing that. But note that there are no obligations nor guarantees. A const method is not obliged to refrain from modifying the object (as the above example shows); the caller of a const method has no guarantee that the object will not change. const is not magic.

I’ve heard some people tout const in C++ as an advantage it has over other languages such as Java or Common Lisp. const methods add contractual guarantees to the language that are useful. Well, there are no guarantees. As with my many “my language is better than yours” debates both sides would probably do well to simply spend more time programming in different languages (not necessarily the ones that are being argued about).

Objective-C doesn’t have const methods, and yet Objective-C programmers using Apple’s Cocoa interfaces enjoy many of the same benefits, in particular a clear separation of mutating and non-mutating methods enforced by the compiler. How is it achieved? Through the class hierarchy. Observe that in C++ that if I have a foo instance then I can call const methods as well as non-const methods, whereas if I have a const foo instance I can only call const methods. A foo instance has all the methods of a const foo instance, and some more on top. This is just like inheritance in an ordinary class hierarchy. If A has all the methods of B and some more methods then we can usually implement this by making A a subclass of B. So it is in Cocoa. The Collections classes illustrate this idea best. Cocoa has an Array class (called NSArray for historical reasons) and a Mutable Array class. The NSArray class models read-only arrays. They can be created (and returned from other methods and functions and so on), but not modifed. The NSMutableArray class is a subclass of NSArray and it supports methods that can modify the array (such as adding, removing, and so on). A method like count, which counts the number of items in the array, is specified by the NSArray class so is invokable on instances on NSArray and instances NSMutableArray. You have an NSMutableArray instance in your hand and you want to pass it to a function which is expecting the read-only version NSArray? No problem, casting up the hierarchy is legal and problem free, just like a cast that adds const in C++.

Some parts of Java’s JSE class hierarchy do a similar thing, see java.awt.image.Raster and its subclass java.awt.image.WritableRaster for example. And of course, Apple’s Cocoa classes (NSArray and friends) also exist in Java as well as Objective-C.

Using the class hierarchy like this might scare you, especially if you have preconceived ideas about what to use a class hiearchy for. To be honest the only people I know to be scared are C++ programmers that haven’t been exposed to enough other object oriented languages.

So, what about mutable? Well, perhaps by now you’ve guessed that I think it’s stupid and wrong and should not be tolerated. It doesn’t add anything that you can’t do with casts or pointers. Perhaps the simplest illustration is putting a pointer to itself in every object:

#include <iostream>

struct silly {
  silly();
  void foo() const;
  silly *const mutate;
  int i;
};

silly::silly(): mutate(this)
{
  i=0;
}

void silly::foo() const
{
  std::cout << i << "\\n";
  ++ mutate->i;
  std::cout << i << "\\n";
  return;
}

int main(void)
{
  silly s;
  s.foo();
  return 0;
}

Every silly object has a mutate variable that points to itself. The const foo method can use the mutate variable to change the object, in this case incrementing i. Calling it mutate even makes it kind of self-documenting. If it’s that easy to get round the const restriction when you want, why bother with an entire new keyword, mutable? The only explanation I have is that the people that have influence over the evolution of C++ enjoy making it more complex in ways that don’t give you any more real programming power.

Of course, as a C++ programmer you need to know about const, and you probably need to know about const methods, but that’s mostly because if you don’t you’ll annoy people that try to create const instances (or refs to such) of your classes.

About these ads

48 Responses to “Stupid const, stupid mutable, stupid C++”

  1. sk Says:

    You’re simply wrong with your mutable critic. C++ compiler is allowed to put const objects in non mutable memory (IOW constant data section). Your code will simply segfault if compiled by such compiler. Mutable is here to tell compiler that such field can be changed.

    So yes, mutable is needed, indeed.

  2. drj11 Says:

    Thank you for your comment sk.

    If I understand you correctly you’re asserting that mutable is necessary because in C++ objects can be declared const and such objects are permitted to be stored in read-only memory.

    I had forgotten that a static object that is declared const can be put in read-only memory in C++. The same is not true in C, hence my oversight.

    You’ll notice that I don’t actually declare any const object in my code, so my code will be fine, even under such a compiler.

    mutable is still not strictly speaking needed even when using an implementation that puts const objects in a read-only section. There are several alternatives. The first and most straightforward is “don’t do that then”. If a class cannot guarantee to operate correctly when its instances are placed in read-only memory then either make it impossible for users of the class to declare one that is const (by not providing any public constructors) or document it as illegal. If that is unacceptable then one could simply place all members that need to be “mutable” into another struct:

    struct silly_m {
    int i;
    };

    struct silly {
    silly();
    void foo() const;
    silly_m *const mutate;
    };

    silly::silly(): mutate(new silly_m)
    {
    }

    void silly::foo() const
    {
    std::cout << mutate->i << “\n”;
    ++ mutate->i;
    std::cout << mutate->i << “\n”;
    return;
    }

  3. Adam Says:

    Most of what you are saying is not incorrect technically, but I think there is a problem with the attitude of “without god, everything is permitted”.

    Just because you can break it doesn’t mean you have to. The language doesn’t determine whether you are a good programmer or not. Writing const-incorrect code doesn’t just annoy people, it’s incorrect 99.9% of the time, and you have to do it intentionally. If you just write code normally the compiler will complain about it.

    While I agree in principle that the language could be tighter, and enjoy programming more in languages that more strictly enforce these rules, I disagree with the assertion that not tightening every screw in the syntax makes a language feature “stupid”.

    Also, re: your comment about static const values not being able to be located in read-only memory in C, I’m not sure where this belief came from. I’ve been programming on multiple embedded systems in C for three years, and my const values always go into ROM, while non-const variables go into RAM.

  4. drj11 Says:

    Re storing const qualified objects in ROM in C. You’re quite right Adam, and now I’ve checked the standard I can’t understand why I’ve been of the opinion that storing const C objects in ROM was a widely implemented flouting of the standard, instead of the permitted behaviour that it actually is. This is good, because it’s sensible.

  5. drj11 Says:

    I never said the language could be tighter. And I don’t believe it. I agree it would be great if a programmer could declare purely functional methods and have their purely functionness guaranteed to be checked by the language implementation. But that’s not going to happen in C++.

    I’m not arguing against const as a language feature (well, not directly, and not in this article), I’m arguing against a certain C++ crowd that tends to tout const methods as a silver bullet. Like I say, having to implement const methods is simply a fact of life once you’ve got const in the language.

    Sure, const methods are helpful. So is documentation. Or choosing a language where you can complete a project with 10 people rather than 100.

    Yes, “stupid const” is an aggressive stance, but it’s makes for a more interesting blog, and it encourages debate. I’m grateful for your comments, because the debate informs.

  6. Adam Says:

    Well, touting anything as a silver bullet usually reveals a lack of understanding of what’s really going on… and usually by bad management (or by a bad programmer).

    Most of my experiences with it have been from management that want to use some new program or tool as a silver bullet to solve some problem (existant or not; they didn’t research it) within the company. Usually, the goal is to attempt to remove humans from the equation by some form of general-purpose automated structure. This almost never works, because A) it doesn’t solve the specific problems of any project in development, and B) humans (especially the expensive ones) usually can’t be removed.

    The worst of all cases though is the development of a tool to replace communication. If communication between people sitting next to each other is already broken, some new tool mucking it up isn’t going to magically fix it…

    What actually got me to respond to your blog in the first place was your first code example in the post. It’s syntactically correct, but such a contrived mangling of constness (referencing a global from inside an instance of a class! arggh) that it seemed like your position was “see, I broke it, ergo it is stupid!”

    Re: keyword “mutable”, I’d actually never heard of this before. I don’t think it’s really common to use this keyword. This is definitely in the category of “syntactic sugar”. It’s one of those things that, when you need it for some reason (debugging a const class by keeping a ref count was one example I found when googling) the keyword simply makes it easier to implement. Why have “for” or “do” when you can write any loop with “while”? Why have “->” when “(*obj).member” works fine? Telling the reader of your code what you are doing as concisely as possible improves readability, so using keyword “mutable” in a class definition as much more obvious than doing a sneaky cast (that you might forget later!) inside the implementation of a funciton.

  7. drj11 Says:

    Adam: are you seriously suggesting that adding a keyword that you hadn’t even heard of a few days ago makes code more readable?

    This is a fragment of a more general argument I have against complex languages. Sure, most complex languages are designed so that their obscure features (mutable, is that obscure?) are optional. Until you have to read someone else’s code. (or in the case of const, have their code call yours)

  8. Adam Says:

    I’m not saying it’s necessarily the case that adding a keyword makes the language better. In the case of “mutable”, I can see an argument either way. (Pro: casting away const is BAD, so we’ll provide a formal way to modify memory in certain circumstances that is predictable. Con: it is too obscure a feature, and therefore it clutters the global namespace and potentially causes the user to have to research the keyword).

    I wanted to suggest a more general argument in response to your comment “If it’s that easy to get round the const restriction when you want, why bother with an entire new keyword, mutable?” with my examples of “for” loops, operator “->”, etc. I see “mutable” as being in this category as it adds, for the user, a semantic meaning that is not present by pretending something is going to be const and then casting it away.

    Where is the line at which you stop adding keywords or features because the language is too complex? As another example, does C# really need a “foreach” keyword when you could just “for” over the size of the container? I’m not saying it should or shouldn’t have it, I’m saying it’s kind of subjective where the line is.

  9. Adam Says:

    “in your response” -> “in response”

    I wish I could edit my post :P

  10. drj11 Says:

    I can edit your post, and I’ve done so, in the manner you suggested. I wish you could edit your posts too. I’ll delete this pair in a few days.

  11. drj11 Says:

    Well, as you say «for», «->», and «mutable» are all in the category of language features that don’t give you any fundamentally new power, they just make some things more convenient. You draw a line in the sand somewhat arbitrarily and decide that some things are okay, and some things aren’t.

    I think C programmers, particular people who still deliberately program in C rather than C++ (that would be me), tend to draw this line so as to minimise language features, whereas people that enjoy programming in C++ draw the line so as to create a richer language. I don’t really have an argument here; if you prefer richer more complex languages then have at it.

    Where I do usually have an argument, and I don’t think you, Adam, fall into this category, is C++ programmers that think their favourite language feature (const, mutable, virtual, templates, whatever) does give them more power, when really it’s just a slightly more convenient (or sometimes, just a slightly different) way of doing things; they just haven’t looked at enough other languages to see how that language solves the same problem. It’s not just C++ programmers either.

    Of course I’m not saying all languages are equal, because I simply don’t believe that.

    As it happens I’d argue that «->» could be removed from C. In the expression «a.b» the type of a, which is statically determinable, governs the meaning. If a is a pointer then you need to derefence to get to the member, if a is a struct then no dereference required. There’s no ambiguity and no problems.

    The language that I prefer to get things done in, Python, has only one form of «for» and it’s like «foreach». Once a language has evolved to the point where it has sensible and convenient container classes that support an iterator protocol then it is sensible to support this protocol as a primitive language feature, «foreach» if you will. At that point you probably find that you don’t use numeric loops much. That’s certainly what I find in Python.

    Specifically in the case of C#, I wouldn’t have an opinion. I don’t do C#.

  12. poo Says:

    That mutable is a hassle for debugging. It says “I’m a constant function so I wont change the object”. The object gets scrambled by some mutable schmutable and you go looking elsewhere. They are a bad idea.

  13. Bunch of stupid idiots Says:

    You are all stupid idiots devoid of epiphany, which if you had received you would not bother dealing with this archaic esoteric inefficient obstinent C++ language.

  14. drj11 Says:

    Chris (aka Bunch of stupid idiots), I don’t see what relevance your points have to the article or the subsequent discussion.

  15. Perfidious Says:

    Actually, mutable is a great addition. As far as I can see, nobody has considered the case where a class has “helper” members that are essential to its function, but that are not actually part of its logical definition. A simple example can be taken from thread programming. For example, I just wrote a thread-safe class with a const function ComputeProgress() that requires the use of a windows CRITICAL_SECTION object, which, of course, has to be non const. This mutex is not actually part of the logical definition of the class, but is required in order for the const function to be thread-safe.

  16. drj11 Says:

    @Perfidious: Why can’t I simply point to the CRITICAL_SECTION object?

  17. Perfidious Says:

    drj11,

    Thanks for replying. This was an old thread, so I didn’t know if anyone cared anymore.

    You could, but then you’d have to assume that the compiler will not be secretly doing something with the CRITICAL_SECTION object that leads to a segfault, or even worse, unpredictable behavior. Keywords like <mutable> were developed for the purpose of writing code that will always behave as you had intended it to, assuming that it is bug free. So, in that sense, it does actually increase the power of the language since it eliminates ambiguity. It would at least make a linguist proud.

    I’m sure keywords like mutable were also included for the sake of both the programmer’s and the code reader’s sanity. I’d be a little concerned if I saw a const object do something non-const, like modify one of its members. By declaring a member mutable, you are telling everyone, including yourself, that you intended to do this non-kosher thing.

  18. Shirish Says:

    Nice discussion …
    I would also love to remove “->”.
    But as per operator precedence I dont know exactly what impact it will have as “->” is having higher precedence than “.”

    But again we also need to think for the need of “.*” , “->*”

  19. name Says:

    c++ is nothing but a bunch of “keywords and descriptions” to remember: const, virtual, pure, extern, register, auto, static, volatile, protected/public/private, friend, inline, new, template, struct/union/class, . . . ad infinitum. c++ is really stupid.

  20. Francois Says:

    For me the main point of const and mutable are to make the purpose of functions and member variables clear to programmers who use the class.

    The moment I see a function declared as

    void someFunction(…) const

    I know the function promises not to modify any member variables of the class it belongs to, or at least not modify them in a way that I, as the user of the class, will notice.

    The mutable keyword has two uses for me:
    1. It makes it clear to me that a member variable has a special function, e.g. caching, and that I should not depend on the variable to remain constant when working with a const object.
    2. It removes the need for const_cast operations in const functions that have to modify such special variables.

    In the end it comes down to preference of language, and the function for which it is required.

    C++ is a very good language, but it takes longer to develop in and requires a lot of discipline to code well. The advantages of C++ is then again low-level control.

  21. drj11 Says:

    @Francois: There are no such promises, haven’t you been listening? If by promise you mean “informal agreement between programmers that cannot be enforced by the compiler”, then sure, it’s a “promise”. But you can get that level of “promise” with simple documentation. And that doesn’t clutter up your language (specification).

  22. Francois Says:

    @drj11:

    I agree with you that, in C++, const is a promise made by the programmer.

    Since static_cast and const_cast can break this promise without any warnings, it is not strictly enforced by the compiler.

    Because of this, you need a lot of dicipline to develop in C++.

    However, I still find the const keyword to be very useful, mainly for these two reasons:
    1. The compiler does catch situations where non-const access is attempted inside a const method. You have to consiously make a decision to const_cast or static_cast to bypass this.
    2. I find it easier to read and understand the code “from the code itself”, instead of having to refer to documentation. If a class definition tells me function is constant, it already tells me a lot about the function. That’s meaningful to me.

  23. Nick Says:

    Not sure how I got here, but a good discussion.

    const is not *just* a promise made by the programmer when you throw in an optimizer. consider:

    int getValue() const;

    The ‘contract’ is not only that getValue won’t change any members. It’s also that at successive calls to getValue() will always return the same value. Thus if you write code:

    for (int i = 0; i < 100; i++)
    if (3 == o.getValue())
    printf("%ld", o.getValue());

    The compiler, because of the const contract, can very well implement this as if you had written:

    int x = o.getValue();
    if (3 == x)
    for (int i = 0; i < 100; i++)
    printf("%ld", x);

    And if you violated the contract you might be in for a world of debugging hurt.

  24. drj11 Says:

    @Nick: Consider the first program in this article. The inspect method is declared const and yet its result can change. I believe this is a legal C++ program. If you do not, please quote chapter and verse.

  25. Nick Says:

    There’s nothing wrong with your program. I said it poorly. It’s not that the value returned by a const method can’t *ever* change. It’s that it ‘can be assumed not to change’ when there are no non-const calls in-between.

    TBH, I’ve never seen the entire specification. I’ve only looked at bits and pieces in various blogs. So you’ll get no chapter and verse from me.

    What I have seen is the intermediate ASM output of my compiler. I’ve seen how the presence or absence of const can produce the changes to the ASM that I described above.

    Now weather the changes can be attributed to const alone, or if const simply triggers a deeper analysis of the method is a question for the code magicians that write optimizers.

    I’m sure you can come up with cases where it does not happen as much as I’m sure I can find cases where it does. So in the end it doesn’t matter if the specification says should or shouldn’t occur. It only matters that in the presence of an optimizer, sometimes it can.

    And here’s an interesting thought. If the language specification determines that “this code will compile to produce that set of instructions” To what extent does it apply to an optimizer that may re-arrange the code before it’s compiled, or re-arrange the resulting instruction set after it’s compiled?

  26. drj11 Says:

    @Nick: I would love to see an example where the ASM changed for merely making a method const or not.

  27. drj11 Says:

    @Nick: Language specs do not generally say “this code compiles to that set of instructions”. Perhaps you should read one. And as for any optimisations that a compiler might perform the “as if” rule applies. You can do any optimisations you like as long as the resulting program behaves “as if” there were no optimisations. Of course, that only applies to _correct_ programs.

  28. Nick Says:

    Again a poor choice of words, but still generally correct. When the specification describes a keyword or other construct, it includes a description about how the compiler reacts when that construct is encountered. How syntax should be validated etc. I call that “generate instruction set” since most of the time that’s what I want my compiler to do with my code. (Customers don’t pay well for syntax errors.)

    The “as if” rule leaves lots of room for differences. One of my favorites is the innocuous

    int x = 0;

    Looks simple enough, but this is often optimized as if it were

    int x = x ^ x;

    Because XOR’ing a register against itself takes fewer machine cycles and less storage than moving a 0 into it from somewhere else (That 0 would have to come from somewhere)

    As for an example, I’ll see what I can find, but being that we’re moving into a holiday, I’m not going to try too hard. Cheers.

  29. drj11 Says:

    @Nick: Right, you mean a spec specifies a translation into machine instructions in the sense that it specifies a possible (very large usually) set of instructions for each possible input program. In other words those instructions that have the intended meaning of the program. I have a blog article about this somewhere. But it was too boring for me to finish, can you believe it?

  30. doc Says:

    I wouldn’t bash C++ for const-correctness and attending programmer to do things in explicit way. Const just fits into C++ philosophy.

    To stand up for C++, Java was so proud to have garbage collector, simple memory management model attending minimal effort from programmer and it ended up with extensive use of dispose(), finalize() etc (in SWT for example) and a lot of problems, since order of destroying objects is not guaranteed. Generics look a lot like modest version of C++ templates, thus Java collections are becoming simillar to STL.

    C++ is like a drudge, it does things in a hard way, but there is less risk that they will revenge oneself. Const may torture noobes but it won’t harm anything important.

  31. Mario Landgraf Says:

    @Nick:
    I’d like to add something to your
    int getValue() const; example.

    The only way the compiler could infer that getValue() always returns the same value is by examining the function’s body.
    const can’t affect this decision as it makes no claim about anything but the method’s this pointer.

    A developer could assume that the return value is always the same by the const keyword and the getFOO name which implies that it’s some sort of getter. But that assumption stands on shaky ground as it could just as easily be a factory method, a method returning a proxy to some other object or a method that returns the current number of users/load/time/etc.

  32. eg Says:

    to “Mario Landgraf”

    you are right..

  33. Douglas Says:

    You wrote: “The key thing is that the conventions are just that, so const methods cannot be relied upon to help you; there is no contractual obligation.”

    C++ const methods would be more useful if the contract was better-enforced, but they’re still useful as-is. Violating the (albeit unenforced) contract requires explicit, deliberate action: either de-const casting or calling another function that uses a global variable (a bad idea anyways). In other words, the function promises not to change anything but might be lying. Arguing that const is useless because it’s susceptible to deceit is like arguing that access modifiers in Java are pointless because access to private members is possible through the reflection APIs. If you don’t trust your associate’s competence, both const and private can help you. If you don’t trust your associate’s intentions, you probably shouldn’t be working with him or her.

    • drj11 Says:

      It’s not always about my associate’s competence is it? Sometimes Someone Else’s Program doesn’t belong to my associate.

      So here’s a more sophisticated version of my argument: “const is bad because it creates a culture where some C++ programmers believe it is a magic bullet”.

      Your Java analogy is slightly flawed. Some Java environments do not have the reflection API (JME. where I do most of my Java programming for example). There the access modifiers are The Law.

      • Douglas Says:

        “Sometimes Someone Else’s Program doesn’t belong to my associate.”
        By “associate”, I mean developers with whom you associate. Whether they contribute to your code or merely developed API you use, they’re your associates.

      • Douglas Says:

        “Sometimes Someone Else’s Program doesn’t belong to my associate.”

        By “associate”, I mean developers with whom you associate. Whether they contribute to your code or merely develop API you use, they’re what I call your associates. If I can’t trust the developers of some API or framework not to deliberately decieve me, I won’t use their code (well, unless I’m forced).

        Your Java analogy is slightly flawed. Some Java environments do not have the reflection API (JME. where I do most of my Java programming for example).”

        Fair enough; I don’t target JME and wouldn’t know. But that’s beside the point: in Java for Java SE > 1.1, the ability to bypass access control does not render access modifiers useless. Indeed, using private and protected members remains extremely useful.

        “const is bad because it creates a culture where some C++ programmers believe it is a magic bullet”

        When a C++ developer calls a const function, s(he) does so knowing that the function _could_ effect changes. But in any reasonable circumstance in which it would, the author of the const function would have deliberately, explicitly, and probably maliciously chosen to do that. I have yet to see such a function. If your argument was “more C++ developers should be aware that a const function can change data/state”, I would understand and probably agree. But I fail to see why const is bad simply because some developers aren’t aware of its shortcomings, or how its shortcomings are anything but negligible.

  34. stormywebber Says:

    It’s been a long time since I’ve learned so much from one entry.

    I found another article that may clear things up a bit — but then again, may not. Regardless, I’m interested in opinions. I’ll include the content (of http://www.builderau.com.au/program/java/soa/C-Tip-Improve-your-code-quality-with-mutable/0,339024620,339129226,00.htm) in case it vanishes later:
    __________________________________________

    … In some cases, however, there’s a distinction between a logical state and an object’s physical state. For example, take an object that represents a graphical image. If the image hasn’t been altered, you would expect its state to remain intact. Yet, in terms of the underlying implementation, large objects often swap their memory into a file after a certain period of inactivity. Swapping an image doesn’t really affect its state, but some of the object’s data members may change, in this case: pointers, flags, and so on.

    When users call a const member function such as Redraw(), they don’t care how this function is implemented internally. From their perspective, this function doesn’t change the logical state of the object and is, therefore, declared const. The fact that it may alter the object’s physical state is an implementation detail that shouldn’t concern them. For example:

    int Image::Redraw() const
    {
    if (isLoaded==false)
    {
    //..read image data from a disk into a local buffer
    isLoaded=true; //changing a data member’s value
    }
    //..paint image in the screen
    }

    Mutable data members
    If you try to compile this code, you’ll receive a compilation error. Although Redraw() is const, it modifies a data member. The solution is to declare isLoaded as a mutable data member:

    class Image
    {
    public:
    int Redraw() const;
    //..
    private:
    mutable bool isLoaded;//can be changed by a const function
    };

    Unlike ordinary data members, a const member function can modify mutable data members.

    The use of mutable data members might seem like cheating because it enables a const function to modify an object’s data members. However, when used judiciously, the mutable keyword improves your code quality as it enables you to hide implementation details from users without resorting to dubious hacks such as const_cast.

  35. H Coles Says:

    This is a really weak argument. The weird little example you use is so arbitrary and pointless; A fringe hiccup if anything. The example Scott Meyers uses is much more applicable to normal usage of C++, in which he shows that the value pointed to by a member pointer can be changed in a const method because C++ objects follow bitwise constness, and since changing the value that a member points to doesn’t change the data of the object itself in memory, it is allowed.

    A word of advice. You don’t like C++, and don’t use C++, that’s fine, it’s an extremely flawed language. But Instead of learning just enough about it to throw together something like this, try looking at the languages you use regularly and are intimately familiar with. Look for flaws in those, and ways to improve them (Big shout out to Java Generics).

    • drj11 Says:

      H Coles: Thanks for your advice, which in fact I have been following for approximately 20 years. My “attacks” on Lisp, JavaScript, Python, C, found on this blog, are all from the position of loving the language, using it, and in the spirit of making it a better place to live and work.

      Possibly the emphasis of this post is wrong, I wrote it as more of an anthropological observation. There is confusion amongst practitioners of C++ (not me) about what const means. My observation (see para 2) was that what the spec says about const and what practitioners say about const vary. And only at most one can be correct.

      If a language’s practitioners and specifications cannot agree then we have a problem! Other languages are of course not without these problems, I have blogged about the difficulties of ‘==’ in Common Lisp, unary plus in JavaScript, and calloc() in C (amogst other fascinating subjects).

      As for Java, see my comment of 2010-01-19. When I do Java I mostly do JME. Good luck reflecting in that.

  36. cygon Says:

    I am perfectly happy with const in C++ and I consider it a strong guarantee.

    Using the backdoor approach (your global variable) is simply locating a code path that lets you reach the object without constness – nothing bad about that.

    Using that to reason that const is no guarantee is akin to saying Unix permissions offer no protection by demonstrating how a normal user cannot stop a service, then logging in as root and stopping the service only to complaining that it is now stopped even though you required the root password. That global variable was your root password.

    Before handing out that global variable, you need to think if you actually want to expose that to the rest of the program, or if the global variable should indeed be const.

    Mutable is nice for lazy evaluation stuff – a const method claims to simply return a result, but actually, it calculates it the first time its called, storing the result in a mutable field. Yes, some discipline is required to keep up the illusion to the outside world and mutable can be misused to break const-correctness for an entire class.

    • drj11 Says:

      I’m not sure I understand you. You consider “const” a strong guarantee, but then give examples where it breaks.

      The global variable thing is a bit weird. My second example, using the mutate member, has no globals at all. Where’s the analogue of your root user in that?

      Perhaps you would like to answer the question in your own terms: “what is a const method?”

      • cygon Says:

        Your second example is just a rehash of the same technique, this time with a member variable instead of a global one.

        If an object is at one time provided a reference to a non-const version of anything, it can store that reference obviously.

        Of course, that goes for the constructor, too – any object can store its own non-const this pointer and that way modify itself. It can also declare all of its own fields as mutable.

        I can see how you believe that this is breaking const.

        To stay with the root user analogy, its a process that can do anything it pleases with itself.

        The only authoritative definition of a const method is in the ANSI/ISO C++ standard, of course, but a good one line that sums up the intention is: “a method that guarantees (I intentionally didn’t say “promises”) that the publicly visible state of the object does not change.”

        It follows that, if you provide one object with only a const reference to another object, it cannot alter the publicly visible state of the other object.

        Each object is free to internally lift these restrictions on its own members to do things like lazy evaluation. A File object may not actually query the size of a file until its “std::size_t GetSize() const” method is called.

        Look at the other languages you mentioned: if in C# and Java you want to expose a list of things the user cannot modify, you have to wrap it in a read-only collection. Oh, but the object exposing the read-only collection can still modify its contents because it has a reference to the mutable collection being wrapped. So does that make read-only collections worthless?

        No, because it’s a tool for the object to control who can do what from the outside. It’s exactly like const, only that the issue has been moved into the architecture space instead of the language.

  37. Martin Says:

    LOL

    But seriously, C++ is a wonderful language, and an ugly one. Love it and hate it. So is PHP with its’ noob oriented syntax that tried to mature along with the acronym, or perl with its’ read-only syntax, or java, or whichever. Maybe one exception, nega, which I bet no one has heard of, good for them then. And with the exception of assembly which isn’t a language per se, it’s a representation, and a good one at that. Back in the days of DOS I wrote a debugger with a 16k memory footprint which fitted a copy of the video, its own stack and all the code for a GUI. That wouldn’t be possible in C, and doubly so in java. What I’m trying to say is that arguing about languages and their relative strengths is stupid without saying what your use case is. If you told me you’ll write my web app in C or that you’d write my new NIC driver in python, I’d give you an equally suggestive look. Not that either can’t be achieved. But it’s more about what I can get the next ego filled dude (it’s rarely a dudette) maintain, and maintain well.

    But seriously, if you don’t like C++, more power to ya, it has many flaws, const methods is not one of them though. Here’s the deal worth a cent perhaps.

    Const methods provide a strong guarantee, in the sense that the compiler will make it difficult for you to shoot yourself in the foot by accident if you declare things right. It can’t guarantee that you won’t aim your gun to your private parts though. If it could or would guarantee any such thing it wouldn’t be C++ it would be some sort of java. In C++ you can write an OS kernel or a JIT compiler, in java you can’t. There are many ways you can violate const correctness, you just cast the pointer to char * and you’re welcome to part with your parts. It’s a feature not a bug. You have placement new, which can be tricky sometimes, and you can use that too to achieve the same result. But the fact remains that the compiler will do its best to enforce the rules. It’s also true that by the very nature of the language those guarantees cannot ever truly be guaranteed. But that’s not the fault of the language. That’s a fault two things, your misunderstanding of what the feature does, and the criteria you’re trying to apply on a language.

    There’s also another misunderstanding in the thread somewhere, and that’s due to one of the genuine faults with C++ syntax, that being the multiple meanings of words like const.

    1) const variable is one which you can’t modify (unless you take pains to aim the gun at your gonads)
    const int x=1;

    2) const method is one which doesn’t change the state of the class, mutable elements not being part of the state
    class x{
    int count() const;
    }

    3) const/pure function, is a function which is purely functional, which is when you promise (and the compiler tries to enforce) that the return value will only depend on the parameters and won’t have side effects, thus the second call to the function with identical parameters need not be made.
    int f(int x) { return x+1; } __attribute__((const));

    I understand how that can be confusing, but it’s all there for a reason.

    I could make the dubious argument that since so many people are using this feature (const methods) and are willing to argue themselves blue about it indicates how useful it is. But I’m not going to, it’s a flawed argument, not as flawed as yours :-) but about as flawed as the best arguments I’ve ever heard for god’s existence. And don’t get me started about the bad ones. Which is pretty much what I think this is, a religious debate over which language sucks more.

    In conclusion, const is good. Not perfect, maybe not even good enough. It doesn’t prevent all self inflicted wounds, but it does prevent many.

    PS, I too am generally more comfortable with C than C++, but I that’s not an argument for or against language features.

    PPS, my main reason for moving to C++ from C was so I can use xxx::some() instead of xxx_do(xxx);

    • drj11 Says:

      “Const methods provide a strong guarantee”. Ah no. You’re illustrating my main criticism with C++: the people who program in it, don’t understand it properly.

      When you say “your misunderstanding of what the feature does”, can you please expand? In particular, what misunderstanding?

      • Martin Says:

        You seem to be laboring under the assumption that const is supposed to be a bullet proof guarantee that a memory location won’t be ever modified. That is not reasonable in most cases. The second problem when criticising C++ is that many people think of it as a full blown object oriented language, it’s not and it’s not meant to be. A full object oriented language, something more like java for example, would only allow you to have object, everything has to be an object and nothing that isn’t an object of some sort is allowed. C++ is a language that uses object orientation as an abstraction, providing interfaces to memory rather than full blown object. Its main advantage is that you can do whatever the hell you want, the main disadvantage is that you can shoot yourself in your favorite body part free of charge.

        For example C++ allows you to make an “object” which will be initialized at a particular location, say the PCIs memory mapped region for a specific device. It can map two separate objects to the same locations, one with some features and the other with all the dirty tricks you desire. The first one would guarantee that you’ll use the interface as intended. This guarantee is strong in the sense that you’ll have to jump through hoops to cast a const object into a non-const, but it’s not strong (by design) in that it allows you to cast it into a rubber duck if that’s what you need. And sometimes you need to cast things around to accomplish your goals. C++ strongly guarantees that you won’t modify a const object, but it specifically and purposely allows you to cast that object into another object with no relation to the previous one, or a non-const object of the same type. That’s not an error, that’s required for some things to be possible. It’s a feature.

        Like the concept of placement new, none of the “true” object oriented languages have it, that’s because they are not designed to write code that can do all C++ does.

        Now if you want a more object oriented language, why not try java, smalltalk or something like that. As long as you don’t mind the slowdown it’s fine. If on the other hand you don’t mind the lack of object orientation and want speed, go for C, which also allows you to write code which violates its own memory model (casting, pointers, aliasing, I’ve seen reasonable enough code have trouble because of the compiler, but that doesn’t mean C is bad, it’s just a low level tool), and with C you can do some object orientation, with some extra pain, myobj_mymethod1(myobj *,…).

        And I’m not saying programming in C++ is easy, or rather, it’s easy to program in C++ but it’s a pain to do it right, requires a lot of discipline. But consider how easy it’s to shoot yourself in some tender place with java when you have threads, and java is allegedly supposed to be bulletproof. Or consider what you get with C, macros to make object orientation less verbose, no type checking in some cases, and you can still cast your “object” (struct *) to a rubber duck. …. that’s not to say C is not a fine language for some things, or java for others, it’s just C++ is there somewhere too.

        I’m not sure this addresses your question, if not please let me know an I’ll add my micro-cent worth.

        PS, I do know C++ well enough, but we apparently disagree about the definition of a strong guarantee. You seem to imply that the language must enforce it no matter what. I on the other hand accept my responsibility to code with care in exchange for the freedom to violate the rules of my interface if I choose to do so. I accept that if I do this irresponsibly I’m the idiot that made the mess. Your definition requires supervised execution, tagged memory or other limitations. Mine exchanges a relaxed definition of “strong” for additional possibilities, like instantiating objects in a mmapped file or on the PCI bus, and the speed. It’s a matter of choice. …. I do stay away from some of the features when I don’t need them. But when I need them I have them.

        PPS, I think the misunderstanding seems to come from the difference between the interface having guarantees and the object having guarantees. In C++ the interface has a guarantee, but you can switch the interface (cast), in java the object can have guarantees, but then you can’t cast a chunk of memory you just read from a file to a buffer as an object. Yes, it’s harder in java to make a mess, but it’s still possible. ….. anyway, in C++ you don’t really have an object, sure there’s a piece of memory which behaves like an object, and sure it has a VTABLE, but it’s only an object because it behaves like there is one. In java you actually have an object. But sometimes I want to know where my object goes, and sometimes I want to avoid the pain that the GC inflicts on people, and sometimes I want to have the speed, and most of the time I am happy with something that behaves like an object as long as I don’t screw it over by idiotic casting and other stupid shit.

        PPPS And no I’m not totally happy with C++, I’m just less impatient with the problems of C++ than those of other languages. For example volatile in C and C++ both is stupid and useless. It means exactly nothing. Java at least has meaningful semantics for volatile.

  38. Sergio Galante Says:

    This article is beyond ridiculous. It’s unbelievably arrogant of you to be going to this length in debating over stuff you are so clueless about.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: