On Thu, 29 Sep 2011 04:12:01 -0400, Peter Alexander <peter.alexander...@gmail.com> wrote:

On 29/09/11 8:13 AM, Marco Leise wrote:
Am 26.09.2011, 18:12 Uhr, schrieb Jonathan M Davis <jmdavisp...@gmx.com>:

On Monday, September 26, 2011 08:01:29 Steven Schveighoffer wrote:
For example, storing a reference to a mutex in an object, or a reference
to an owner object. It's the difference between a "has a" relationship
and a "points to" relationship.

Your lazy loading idea does not help at all for these.

I believe that the two main complaints about the lack of logical const
which
have been coming up in the newsgroup have been the inability to cache the
return values of member functions and the inability to lazily load the
values of member variables.

I brought the issue with "points to" relationships up in another thread
here - also about transitive const. I believe a proper object-oriented
design should allow for pointers to other objects that have nothing in
common with the host object. car.getVendor() for example should always
return the mutable instance of the shop where that - possibly const car
- was bought. Let's call it the "parent reference" issue for short :)
+1 to Steven's comment.

Why would a car be able mutate it's vendor?

Because a vendor is not part of a car.  It's a link.

In logical const land, the point of the piece of logical const data is that it's not part of the object's state. It's a piece of data *related* to the object, but not *part* of the object.

My favorite example is a Widget and a window to draw the widget in. The primitives for drawing in the window obviously cannot be const, because they alter the window. However, it makes complete sense that when drawing an object, you don't want it's state to change. So how to define the widget?

Window w;
const draw();

This doesn't work, because the window is a part of the widget state, and therefore const. So you can't draw anything in the window inside the draw function.

There are two possible solutions. First is, just make it not const. But then you lose any guarantees that the compiler gives you about not having the object state change. Second option is to pass the window into the widget:

const draw(Window w);

But then, it becomes cumbersome to drag around a window reference everywhere you have a Widget reference.

What logical const does is allocate space that is *associated* with the object, but not *owned* by the object. It actually doesn't matter if it lives in the object's block or not.

Also, the problem with mutable parent references is that it completely defeats the purpose of const. Consider this C++ code:

struct Tree
{
     Tree* m_parent;
     std::vector<Tree> m_children;

     void doSomethingConst() const;
};

The tree owns it's children, and has a mutable reference back to its parent.

Inside doSomethingConst, I shouldn't be able to modify my children because I own them, however, you are arguing that I should be able to get a mutable reference to my parent. However, if I can get a mutable reference to my parent, then I can get mutable references to its children, of which (this) is one. So, through my parent I can get a mutable reference to myself, circumventing the const, making it pointless.

This is a tricky situation. I think logical const only makes sense if there are no cycles. That is, in my widget example, the window doesn't know about the widget, and does not have a pointer back to it.

I think in your example, the parent should be part of the tree state, since it contains itself. Therefore, logical const should not be used.

Such a thing is impossible to prove by the compiler, so logical const does break the compiler guarantees. There is no way to allow logical const in the case of the widget, and not allow it in the case of the Tree struct.


Strict transitive const is essential. Without it, it becomes far too easily to wriggle your way out of const, which breaks the guarantees of immutable.

This is not true. An immutable object is never mutable, and could never be assigned to a mutable reference, even if that reference was inside a logically const object.

Logical const only comes into play when you are talking about mutable pieces that are temporarily cast to const.

-Steve

Reply via email to