Re: Using a macro to simplify implementing non-const methods in terms of const overload
Hello Bart,
No matter how trivial the code, applying a const_cast<Foo*> to an object
defined as 'const Foo bar;' already lands you in the land of UB.
After that point, whatever the compiler does to your code, it is correct
by definition.
Fair enough. The standard does say UB esp. 7.1.5.1 & const_cast bit.
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
4 A constructor shall not be virtual (10.3) or static (9.4). A constructor
can be invoked for a const, volatile or
const volatile object. A constructor shall not be declared const, volatile,
or const volatile (9.3.2). const
and volatile semantics (7.1.5.1) are not applied on an object under
construction. They come into effect when the
constructor for the most derived object (1.8) ends.
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?
Ok, to do the const_cast is UB according to the spec. but it simply must be
a proper object with real writable storage. 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.
Perhaps the extreme trivial example is bad anyway because it is less typing
to reimplement the functions...
(make _Count mutable)
Thing& bar(int n) {++_Count; return _V[n];}
const Thing& bar(int n) const {++_Count; return _V[n];}
is shorter than casting anyway. Even more so if you don't have a count.
It does have it's uses though. 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.
Chris
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]