www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Frustrations with const

reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
I'm writing an AA implementation, and ran into this problem with the way
const behaves in D. Basically, I have an auxiliary function that
searches the internal hash table for a given key, and returns the slot
containing the matching entry, if found.

The problem is, how to write this function so that it can be called from
*both* a const public method and a non-const public method? Since the
method itself doesn't actually modify anything, it *should* in theory be
possible to mark it as const:

	const Slot *findSlot(Key key) { ... }

However, because the const applies to 'this', the compiler insists that
referencing anything via 'this', including reading a pointer to a slot,
must also be const, so it refuses to let the return type be Slot*; it
has to be const(Slot)*.

But this is silly, because now the caller isn't allowed to modify the
Slot either, so now I need to bloat the code with two identical copies
of findSlot, one with const, and one without (since if it wasn't marked
const, then a const method couldn't call it).

Is there any way to work around this?

Or more importantly, why does the compiler force all internal members of
this to be const inside a const method, when what it really should be
enforcing is that nothing is being *modified*? What's wrong with
extracting non-const members from the object in a const method, if you
never actually do anything to it?


T

-- 
Never wrestle a pig. You both get covered in mud, and the pig likes it.
Mar 08 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/08/2012 08:09 PM, H. S. Teoh wrote:
 I'm writing an AA implementation, and ran into this problem with the way
 const behaves in D. Basically, I have an auxiliary function that
 searches the internal hash table for a given key, and returns the slot
 containing the matching entry, if found.

 The problem is, how to write this function so that it can be called from
 *both* a const public method and a non-const public method? Since the
 method itself doesn't actually modify anything, it *should* in theory be
 possible to mark it as const:

 	const Slot *findSlot(Key key) { ... }

 However, because the const applies to 'this', the compiler insists that
 referencing anything via 'this', including reading a pointer to a slot,
 must also be const, so it refuses to let the return type be Slot*; it
 has to be const(Slot)*.

 But this is silly, because now the caller isn't allowed to modify the
 Slot either, so now I need to bloat the code with two identical copies
 of findSlot, one with const, and one without (since if it wasn't marked
 const, then a const method couldn't call it).

 Is there any way to work around this?

 Or more importantly, why does the compiler force all internal members of
 this to be const inside a const method, when what it really should be
 enforcing is that nothing is being *modified*? What's wrong with
 extracting non-const members from the object in a const method, if you
 never actually do anything to it?


 T

inout(Slot)* findSlot(Key key) inout { ... }
Mar 08 2012
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 03/08/2012 08:49 PM, H. S. Teoh wrote:
 On Thu, Mar 08, 2012 at 08:22:05PM +0100, Timon Gehr wrote:
 On 03/08/2012 08:09 PM, H. S. Teoh wrote:

 The problem is, how to write this function so that it can be called from
 *both* a const public method and a non-const public method? Since the
 method itself doesn't actually modify anything, it *should* in theory be
 possible to mark it as const:

 	const Slot *findSlot(Key key) { ... }

 However, because the const applies to 'this', the compiler insists that
 referencing anything via 'this', including reading a pointer to a slot,
 must also be const, so it refuses to let the return type be Slot*; it
 has to be const(Slot)*.

 But this is silly, because now the caller isn't allowed to modify the
 Slot either, so now I need to bloat the code with two identical copies
 of findSlot, one with const, and one without (since if it wasn't marked
 const, then a const method couldn't call it).


 inout(Slot)* findSlot(Key key) inout { ... }

Ahhh. Thanks! But that still doesn't solve the problem: inout(Slot)* findSlot(Key key) inout { auto slot = slots[hash(key)];

inout(Slot)* slot = slots[hash(key)];
 		while (slot) {
 			if (slot.hash == hash(key)&&  slot.key == key)
 				return slot;

 			// Error: cannot modify inout(Slot)*
 --->			slot = slot.next;
 		}
 		return null;
 	}



 T

Mar 08 2012
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 03/08/2012 09:06 PM, H. S. Teoh wrote:
 On Thu, Mar 08, 2012 at 02:50:50PM -0500, Steven Schveighoffer wrote:
 On Thu, 08 Mar 2012 14:49:12 -0500, H. S. Teoh
 <hsteoh quickfur.ath.cx>  wrote:

 On Thu, Mar 08, 2012 at 08:22:05PM +0100, Timon Gehr wrote:


 inout(Slot)* findSlot(Key key) inout { ... }

Ahhh. Thanks! But that still doesn't solve the problem: inout(Slot)* findSlot(Key key) inout { auto slot = slots[hash(key)];

What is type slot, and how is it constructed? This snippit isn't enough to provide help.

Slot is a struct, and slots is Slots*[]. But anyway, I found the problem. I needed to explicitly declare slot as: inout(Slot)* slot = ... because for whatever reason, auto turns it into inout(Slot*) which cannot be modified.

Probably it does not. What is the type of the "slots" hashtable?
 I think inout is one of those things that really needs more thorough
 treatment for newbies, because coming from a C/C++ background I had no
 idea what was wrong. Now that I get it, it makes so much more sense.


 T

This is a general issue with how auto infers types. It would often be useful to get the head-mutable type out of the type deduction instead of the actual type.
Mar 09 2012