Mark said:
....
Thanks to both you and Victor. That clears it up very well.
One more thing (with apologies to Steve Jobs),
Note: what follows is probably basic on comp.lang.c++, but the thread is
crossposted with alt.comp.lang.learn.c-c++ and I'm leaving the former
in, for now, for thread completeness. If this will spawn a longer
subthread we may want to break out and stop crossposting.
I've been thinking why you would want the A to be constant too and if
this made sense, and how you would actually do it if you need A to be
constant when seen throu a constant B.
Consider:
class A {
public:
virtual void mutate();
virtual ~A()=0;
static A* New( /* some params */ );
protected:
A();
A(const A&);
A& operator=(const A&);
};
The above is somewhat "classic". You probably recognize some patterns.
A is the (abstract) base of a hidden hierarchy. You need not know the
actual implementation classes: you instantiate only through A::New. And
that gives you a dynamically allocated instance of some class derived
from A.
Now for B:
// we have an A, and it's "value" is part of our "value"
class B {
public:
A a; // error
};
There are many possible reasons to have an A bound to a B. Here a B "has
an" A as part of its 'value' (or 'state' or whatever). But it does not
work because we can only have dynamic As. Therefore we go with something
like you did in your OP:
class B {
public:
A &a;
B()
:a( * A::New(/*params*/) )
{
}
~B() { delete &a; }
};
I hope the reason you used the *ugly* initializer '*new A' is not this
one but just a way to represent your code without a lot of details and
you actually have the reference initialized from a parameter or
something. But I'll do the same: I'm just making a completely different
point and the above code will go away in a moment.
Now the above works. Or does it?
We have the exact problem you asked about:
void foo( const B& b )
{
// this violates contract
b.a.mutate();
}
As the A is part of the "value" of B, you are changing the "value" of a
supposedly constant object.
How do we solve this?
That is: how do you make sure the A is also seen as constant if the B is
such?
This is were function overloading comes to the game. And the reason
accessors are much superior to exposed data members:
class B {
A *pa;
public:
B()
a( A::New(/*params*/) )
{
}
~B() { delete &a; }
A& a() { return *pa; }
const A& a() const { return *pa; }
};
Sure you now need an extra pair of character to access the A, so the
interface is not exactly the same.
But this is how it should have been from day one.
Not sure if and how the above applies to your program, but it is closely
related to the kind of surprise you had in the OP.