Re: Using a macro to simplify implementing non-const methods in terms of const overload
Chris Morley wrote:
Hello Bart,
Hello Chris,
The compiler is fully within its right to mark the memory page that
contains the object t as being read-only. If it chooses to do so, any
usage of the function foo:bar() will most likely result in a crash.
And even if it does not choose to make t physically read-only, the
compiler can do whatever it wants when you cast away the constness.
In real life this isn't going to happen though is it? The class has a
non-trivial constuctor and the instance must be a temporary. It is OK
for the constructor to modify the members - requiring proper writable
storage. 12.1
Who know what the next compiler is going to do.
Also note that on modern processors, a memory page can be marked as
read-only at any time. So the compiler can give that instruction right
after the constructor finished.
And perhaps a next generation of processors allows a much finer
granularity in what is marked as read-only, read-write and/or
executable.
<snip>
so..
class foo {
public:
foo():_Count(0) {++_Count;}
int count() const {return _Count;}
int _Count; // doesn't even need a mutable
};
int main()
{
const foo f;
int k=f.count();
}
Is well formed isn't it?
Yes, that is well-formed code that must initialise k with the value 1.
Ok, to do the const_cast is UB according to the spec. but it simply
must be a proper object with real writable storage.
No, that is not true. Under the "as-if" rule, if a conforming program
(without UB!) can't tell the difference, the compiler is free to do what
it likes.
So, in this example, the compiler can transform the code to:
int main()
{
int k=1;
}
And even if the address of f was taken (so the object can't be
eliminated entirely), f could have been allocated in read-only storage
(by transforming the constructor that _Count is initialised to 1).
So while it might
be strictly "UB" the behaviour is determined. It may be ill advised
but it will work. Who would do such a thing with a member which isn't
trivial anyway - that is asking for trouble.
Well, rule number 1 for programmers should be: "Don't try to outsmart
your compiler. It _will_ get its revenge on you."
I consider this a prime example of trying to outsmart your compiler.
<snip>
It does have it's uses though.
Sure. Otherwise it would not have been part of the language. :-)
I've used it in a "lazy load" wrapper
for accessing a pointer to a runtime polymorphic classes instantiated
from DB data (amongst other function). The DB load is the slow part so
that bit only runs if the pointer to the constructed class (based on
db data) is actually used. The wrapper is compile time constant size
and has no heap storage - a trait I want. I know all decls are
temporary (non-const) or in e.g. vector. Some code gets passed a
const& to the wrapper. It was simplest to const_cast just the const
member but with hind sight I suppose I could have consted everything
and mutabled all the member variables - more typing :( I know this
const_cast is safe but mutabling everything needed means it is easier
to "oops" in another fuction which _shouldn't_ be allowed to modify
them. Lesser of two weivels.
As you know that in that case all objects are physically non-const, the
const_cast is safe.
And as the load_from_db function will modify just about every member of
the class and it is the only function that should do so when used on a
const-qualified reference, using a cons_cast there is entirely
appropriate IMO.
Chris
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]