the updateCachedWidth example probably gives the wrong idea to a lot of folks.
You would be just as well off making computeWidth const/pure/readonly/whatever
The compiler can even detect if it modifies anything and mark it for you.
In fact, better compilers will compute mod/ref information and know that m_cachedWidth is not touched over that call.
However, LLVM's (which is what at least Apple is using) basic stateless alias analysis is not good enough to do this in this kind of case (there are some exceptions where it might be able to, none apply here)
This is actually a great example of how improving pointer and alias analysis in a compiler buys you gains, and not an example of "how you should modify your code", since you generally should not modify your code to work around temporary single-compiler deficiencies without very good reason.
Especially considering how quickly compiler releases are pushed by Apple/et al.
I agree with the 'make things const' part, but I would expect m_cachedWidth to be mutable. I'm too lazy to check that now, but if so, that would not help here.
Even if it is not, I still think this is an example of "how you should modify your code". Reason? Doing
temp = foo();
temp *= bar();
m_member = temp;
keeps your state consistent in case bar() throws. I would even use it if bar() is known not to throw, because you can't know what the future will bring, and because what you 'know' sometimes isn't true. Defensive programming, if easy as in this case, is a net benefit.
True enough, but we strive for consistent practice - coding in one way for project X, another for project Y, is error prone and doesn't scale. That handy dandy defrobolizer you wrote for WebKit? You can't really reuse it in another project without rewriting it to be exception safe. You should vary from good practices only when it makes sense, not use them only if you can make a case for using them in this specific instance. Anyone can come along later, eyeball your code, and understand your intent and see the potential problems. Magically code for a very specific environment? Good luck. Also, good luck when your next project requires exceptions or what have you, because you will be deeply out of practice coding for them.
Obviously the above can be taken too far, but programming for const correctness, mutability, and safety in the face of exceptions should be in everyone's toolkit, and the default way they program in C++ (IMO, naturally).
Keep in mind the example is just there to illustrate a type of problems. The real use cases were obviously more complicated.
Some comments:
The compiler does not do magic. If you call a virtual function or call an address from an other library, there is simply no way for the compiler to know the side effects at compile time.
The keyword "const" does not help the compiler to optimize this kind of code. Const is mostly a tool for developers, you can ignore it with mutable, const_cast, etc.
In many cases, the kind of code of the example will be optimized properly:
-If the code is simple enough and all the functions are in the same compilation unit, Clang will figure out the dependencies and optimize it properly.
-If the code is simple enough and the code is spread across several compilation units, Clang's link-time optimizer does an amazing job at finding dependencies.
The point of this example was more to illustrate a point about code clarity. We should not hesitate to make code more explicit, use extra temporary variables, etc. Those extra lines help both the other hackers and the compiler.
"Some comments: The compiler does not do magic. If you call a virtual function or call an address from an other library, there is simply no way for the compiler to know the side effects at compile time."
False. There are annotations on plenty of these libraries in header files, and you can determine virtual call side effects in plenty of cases through various forms of analysis.
"The keyword "const" does not help the compiler to optimize this kind of code. Const is mostly a tool for developers, you can ignore it with mutable, const_cast, etc."
Let's just say I understand what const and the various attributes I mentioned do, where they are useful, and how compilers use them. I disagree with your statement. Note that const where i mentioned it (IE computeWidth() const) would make the this pointer const, and const_casting that would not be legal, AFAIK.
Unless the member variable was marked as mutable, the compiler would know it is readonly.
"The point of this example was more to illustrate a point about code clarity. We should not hesitate to make code more explicit, use extra temporary variables, etc. Those extra lines help both the other hackers and the compiler."
This is not what was written in the article (though i'd agree with your statement about code clarity) , and as I showed, those extra lines do not help a good compiler.
Wrong const. He's talking about the gcc function attribute extension, which most of the time is too strict, so recommending its general use isn't a great idea.
C++ const has no effect on optimization, since it can be casted away.
And being aware of aliasing issues is a good idea in general; nothing the compiler does can fix this in the general case (e.g. if computeWidth() is located in an external shared library it's basically impossible for the compiler to determine that it can't modify m_cachedWidth)
"And being aware of aliasing issues is a good idea in general; nothing the compiler does can fix this in the general case (e.g. if computeWidth() is located in an external shared library it's basically impossible for the compiler to determine that it can't modify m_cachedWidth)"
This is of course, false, it would be more accurate to say "in most open source compilers, ....".
Doing compile/dynamic link/ analyze based points-to analysis is at least 15 years old (papers published), and likely much older.
There is nothing that fundamentally stops the compiler from knowing things about external shared libs. At some point, the binary is being linked against those libs. You can store the necessary info in those libs, and then use it at link time
(This is in fact, one of the premises of LLVM and having a good, cheap, complete on-disk representation).
In any case, in this example,
1. you don't need the function attribute, just the normal C++ function modifier (It's too early to remember if that is the right C++ terminology) would do, since m_cachedWidth is clearly a member.
Link time for shared libraries is run-time, so unless you're advocating everything be JITed the compiler still cannot know in the general case whether an external function modifies random memory.
1. I'm relatively sure that const member functions are still allowed to write into pointers that could potentially lead back to the object.
2. No way to know that just from the source. No way to know it's a member function either - if it isn't then 1. is irrelevant.
C/C++ const has no effect on optimization, unless it's on a global/static object and the compiler can see the original declaration.
I'm not going to look it up in the C++ spec, but in C it's only undefined behavior if the original object was const, and it would make sense for C++ to be the same.
But if you have a const reference to a non-const object, it's legal to use a const_cast to remove the const part. This is the more likely scenario, since most objects aren't const.
You would be just as well off making computeWidth const/pure/readonly/whatever
The compiler can even detect if it modifies anything and mark it for you. In fact, better compilers will compute mod/ref information and know that m_cachedWidth is not touched over that call.
However, LLVM's (which is what at least Apple is using) basic stateless alias analysis is not good enough to do this in this kind of case (there are some exceptions where it might be able to, none apply here)
This is actually a great example of how improving pointer and alias analysis in a compiler buys you gains, and not an example of "how you should modify your code", since you generally should not modify your code to work around temporary single-compiler deficiencies without very good reason.
Especially considering how quickly compiler releases are pushed by Apple/et al.