One problem with const-correctness is C++ uses "const&" to mean two different things: it could mean either a reference to a persistent object you're not allowed to change, or it could refer to a temporary object which you're prevented from changing because the changes will disappear. The problem is it conflated access control with lifetime. It would have been better if there were a "temp" keyword - although the last thing C++ needs is more combinatorics of type decorators.
On the subject of consts, though, C++ simply enforces const correctness more, but doesn't the same criticism of confusing const apply to pointers in C as well? You can just as easily declare a "const * const" in C as in C++. So I fail to see why Zed Shaw writing something in C gets him away from that "problem", unless he doesn't write const correct C.
The fact that it's C++, where the prototypical user believes that they shouldn't pay for what they don't use just makes things worse. Everything being optional means there can be very few synergies in language or standard library design, because if feature A needs feature B to work, people who want A but not B will complain. C++ "chooses" either by omitting both A and B, or creating some low-level error-prone features and hoping users won't chop off too many fingers trying to build their own incompatible versions of A and B. An ill-designed misfit language is all but required by the dynamics of the situation.
If you do like const (which I do) then you can put it in pretty much everywhere, except if you are dealing with a library that isn't "const correct", but then a bit of type casting will save the day.
That's a big advantage of lots of the C++ features - if you don't like them you can often just ignore them.
In retrospect, it might have been better to have a keyword for things that can change. This goes equally for Java and "final". Making immutability the default and mutability opt-in makes for fewer bugs and more reliable programs.
Arguably, this is what Clojure does where you have functional, immutable constructs by default with ways to declare things that can change (refs, etc.).
The thing is, this kind of code isn't an exception in the C++ world. C++ is made for problems where performance matters: image processing, compilers, browsers... immutability works for Clojure, but Clojure is a lot slower than C++. It's more of an "enterprise software" language.
* Use const everywhere
* Use casts everywhere
The same thing happens if you use const and your libraries don't...
For those with a slightly broader perspective and an inquisitive mind, perhaps you should take a look at at the Reason C++ framwork, specially if you have never seen C++ in its most pure uncomplicated OO form.
Reason is a C++ framework that ive been writing for about 8 years, which aleviates many of the sources of pain highlighted in this discussion. Writing code with Reason is much more like using a dynamic language, with a full library like Java or .Net.
It supports raii, but doesnt use exception handling and doesnt bother with const nonsense or over the top inheritance restrictions. In fact pretty much everything in Reason is public and designed to be derived, modified or enhanced.
It has generic programming features, but doesnt force you to write everything as templates (a classic mistake that the standard library makes). For example, you can have an iterator of int's without caring what the underlying container type is, so your code and algorithms can actually be generic, not just infectious templates.
Unlike a lot of C++ frameworks and libraries, its not specifically focused on networking, or a few esoteric template classes or collection libraries.
Reason is a fully featured systems programming framework with a complete interface to all the usual posix api's and everything else you would expect from a modern language. It has strings, regexes, streams, parsers, sockets, threads, filesystem, encryption, encoding, xml, xpath, collections, time and date, sql (mysql, postgress, sqlite), http (client/server), smart pointers, formatting, logging, and much more.
But the features are perhaps not as important as how it is written. It is simple, and very object oriented, everything is designed to work togeather cleanly and obey the principle of least surprise. Just like Python and Ruby, Reason allows you to do a lot in a very small amount of code.
So if your wondering how you can have the simplicity of C, with just the good parts of C++, heres your answer.
I think it is so interesting the mentality of static typing people. I would much prefer handling the few resulting run time faults than having to stuff with my code with const.
Anyway, there is no guarantee in C++ that something that compiles with all consts in the world will not throw a segfault at run time in the least expected moment...
Of course, one problem with C++ (and, to a lesser extent, C) is that many of those subtle correctness issues are direct results of the type system in the first place. Go figure.
The beauty of dynamic typing is that it makes the writing of the code easier, which makes development shorter, which gives you more time to thoroughly test your code. After years leading a QA team, I'm convinced that more time testing is always superior to more time writing code, even if the code you're writing is there to stop bugs!
However, to support what Zed is saying, Scott Myers (of Effective C++) was astonished about some of the things done there.
C++ doesn't follow the principle of Least Astonishment in any way that I can think of. And that is not good.
Loki is neat, even if it does represent everything that is evil about C++.
* C++ circa 2000 (before mainline g++ could handle Alexandrescuisms) is significantly different from C++ circa 2010, albeit in ways that probably upset Shaw even more (the more central role boost has taken, the more "expressive" templates have gotten, don't call me on any of this stuff).
* C++ std::string is an abomination, but you can always just do what I've done and what lots of other C++ programmers whose code I've read have done: just use char* and a simple string library (or a custom string class).
* Ditto for stream IO, which is a huge C++ misfeature but which is also pretty much irrelevant (I know of no part of the standard C++ library outside of things that explicitly support stream I/O that rely on you using it).
* I don't get the POSIX argument at all; just call the POSIX functions, they work fine. Nobody mandates that you use (say) ACE_wrappers to do systems programming.
* const-correctness may be another misfeature (I know I make fun of it), but the point isn't hard to see: if you take the time to const-correct your code, the compiler will spit out errors that would have otherwise been runtime faults.