Thread overview
const and non-const member function
Jul 25, 2007
Frank Fischer
Jul 25, 2007
Christian Kamm
Jul 26, 2007
James Dennett
Jul 26, 2007
Christian Kamm
Jul 26, 2007
Frits van Bommel
Jul 26, 2007
Christian Kamm
Jul 26, 2007
Kirk McDonald
Jul 26, 2007
Regan Heath
July 25, 2007
Hi,

I'm just playing around with const in DMD 2.003 and I have a question.
In C++ it is possible to have a const and a non-const member function with
the same name and parameter types. It depends on the type of the reference
to the object which function is called:

class A {
public:
  int myfunc() { return 0; }
  int myfunc() const { return 1; }
};

A a;
a.myfunc(); // returns 0
const A& b = a;
b.myfunc(); // returns 1

Is something like this possible in D?

Another example would be with different return-types:
class B {
  A a;
public:

  A& myfunc() { return a; }
  const A& myfunc() { return a; }
};

(of course, in D you don't need explicit references but the idea is the
same).

Furthermore, is it possible to overload opApply(...) so it can be called on non-const objects and const objects, and the non-const variant has read-write access:

foreach(ref x; obj) x = 1; // possible for non-const 'obj'
foreach(ref x; cast(const(Obj))obj) x = 1; // ERROR for const 'obj'
foreach(x; obj) writefln("%d", x); // Ok for both, const and non-const 'obj'

What would be the D-way of doing this? I need this access to provide the correct member functions in some of my D-libraries I write, since I want to have const-versions of my objects.

Thanks,
Frank

July 25, 2007
> In C++ it is possible to have a const and a non-const member function with
> the same name and parameter types.
> ...
> Is something like this possible in D?

I don't think it's possible (yet). Trying

class Foo
{
  void bar() {}
  const void bar() {}
}

gives me an error. I'm not sure if it is by design or just not working yet.

> Furthermore, is it possible to overload opApply(...) so it can be called on non-const objects and const objects, and the non-const variant has read-write access:

Having that would pretty much depend on being able to have a opApply and a const opApply. I tried by using two functions with different names, but the compiler segfaulted on a ref const(Struct*).

Cheers,
Christian
July 25, 2007
Christian Kamm wrote:
>> In C++ it is possible to have a const and a non-const member function with
>> the same name and parameter types. ...
>> Is something like this possible in D?
> 
> I don't think it's possible (yet). Trying
> 
> class Foo
> {
>   void bar() {}
>   const void bar() {}
> }
> 
> gives me an error. I'm not sure if it is by design or just not working yet.
> 
>> Furthermore, is it possible to overload opApply(...) so it can be called
>> on non-const objects and const objects, and the non-const variant has
>> read-write access:
> 
> Having that would pretty much depend on being able to have a opApply and a
> const opApply. I tried by using two functions with different names, but the
> compiler segfaulted on a ref const(Struct*).
> 
> Cheers,
> Christian

I may be wrong, but I think the idea is that a method either is or isn't const-safe, and the difference between const and mutable references to the object is supposed to be in the availability of methods, not in their behavior.  While I'm sure there's some percentage of the time subtle behavioral changes could be desirable, I don't believe const-method overloading is meant to happen.

That said, a few demonstrative cases sometimes do wonders.  :)

(I'm also not 100% pleased with the 'const R M(){}' syntax for declaring const methods...  To me it reads as a function M() returning 'const R'.  Yes I know the syntax for that is 'const(R) M(){}' and no I don't have a better idea right off hand.  I don't care for the C++ 'R M() const {}' either.  Wasn't there something like 'R M:const(){}' volleyed back when the const discussions first happened?)

-- Chris Nicholson-Sauls
July 26, 2007
Chris Nicholson-Sauls wrote:
> Christian Kamm wrote:
>>> In C++ it is possible to have a const and a non-const member function
>>> with
>>> the same name and parameter types. ...
>>> Is something like this possible in D?
>>
>> I don't think it's possible (yet). Trying
>>
>> class Foo
>> {
>>   void bar() {}
>>   const void bar() {}
>> }
>>
>> gives me an error. I'm not sure if it is by design or just not working yet.
>>
>>> Furthermore, is it possible to overload opApply(...) so it can be called on non-const objects and const objects, and the non-const variant has read-write access:
>>
>> Having that would pretty much depend on being able to have a opApply
>> and a
>> const opApply. I tried by using two functions with different names,
>> but the
>> compiler segfaulted on a ref const(Struct*).
>>
>> Cheers,
>> Christian
> 
> I may be wrong, but I think the idea is that a method either is or isn't const-safe, and the difference between const and mutable references to the object is supposed to be in the availability of methods, not in their behavior.  While I'm sure there's some percentage of the time subtle behavioral changes could be desirable, I don't believe const-method overloading is meant to happen.
> 
> That said, a few demonstrative cases sometimes do wonders.  :)

The normal reason (coming from C++) for having overloads by constness is when the return value should differ in constness.

The C++ community would frown on code with differences between const and non-const member functions other than those that directly relate to constness.

A trivial almost example is any object returning a reference to a component of itself; if the object is const, then the reference to its component should not allow modification, but if the object is non-const, then modification of its component may well be reasonable.

(There's a separate debate that I don't want to get into about
whether it's poor design to expose references to components.
If that's a sticking point to you, imagine that the relationship
is "uses" rather than "is composed of".)

-- James
July 26, 2007
> I may be wrong, but I think the idea is that a method either is or isn't const-safe, and the difference between const and mutable references to the object is supposed to be in the availability of methods, not in their behavior.  While I'm sure there's some percentage of the time subtle behavioral changes could be desirable, I don't believe const-method overloading is meant to happen.
> 
> That said, a few demonstrative cases sometimes do wonders.  :)

Well, the original poster basically wanted it to be possible to have

class Container(T)
{
  int opApply(int delegate(ref T) dg);
  const int opApply(int delegate(ref const(T)) dg);
}

such that foreach would work on const(Container!(T)) and Container!(T)
alike, using the correct types. To me, that seems pretty reasonable.

James Dennett pointed out another, simpler example

Foo getThis() { return this; }
const const(Foo) getThis() { return this; }
invariant invariant(Foo) getThis() { return this; }

Personally, I'd like to see the 'templating on const' solution that was thrown around during one of the const debates

C C(Foo) getThis(constness C)() { return this; }
and
C int opApply(constness C)(int delegate(ref C(T)) dg)

as the different versions are usually quite similar, except for slightly different types.

> (I'm also not 100% pleased with the 'const R M(){}' syntax for declaring
> const methods...

Yes, it's quite bad:

const Foo func(const Foo)
{
  const Foo = ...;
}

All consts look the same, but the first means the method, the second is an
alternative for const(Foo) and the third is the compile-time const storage
class.
I'd probably disallow const Foo in parameter declarations, yet don't know
how to do make the method-const look different.

Cheers,
Christian
July 26, 2007
Christian Kamm wrote:
>> (I'm also not 100% pleased with the 'const R M(){}' syntax for declaring
>> const methods...  
> 
> Yes, it's quite bad:
> 
> const Foo func(const Foo)
> {
>   const Foo = ...;
> }
> 
> All consts look the same, but the first means the method, the second is an
> alternative for const(Foo) and the third is the compile-time const storage
> class. I'd probably disallow const Foo in parameter declarations, yet don't know
> how to do make the method-const look different.

How about this for method-const:
---
Foo const(func)(const Foo) {
    // ...
}
---
Though I guess that may look too much like the const(Type) notation...
July 26, 2007
>> I'd probably disallow const Foo in parameter declarations, yet don't know how to do make the method-const look different.
> 
> How about this for method-const:
> ---
> Foo const(func)(const Foo) {
>      // ...
> }
> ---
> Though I guess that may look too much like the const(Type) notation...

I thought the same thing: putting the const/invariant after the return type might be okay

---
Foo const func(const(Foo))
---

but the current way of doing it has the merit of allowing

---
const {
 void func1();
 void func2();
}
---

which'd be odd to explain with the const being anywhere but in the very front.

July 26, 2007
Frits van Bommel wrote:
> Christian Kamm wrote:
>>> (I'm also not 100% pleased with the 'const R M(){}' syntax for declaring
>>> const methods...  
>>
>> Yes, it's quite bad:
>>
>> const Foo func(const Foo)
>> {
>>   const Foo = ...;
>> }
>>
>> All consts look the same, but the first means the method, the second is an
>> alternative for const(Foo) and the third is the compile-time const storage
>> class. I'd probably disallow const Foo in parameter declarations, yet don't know
>> how to do make the method-const look different.
> 
> How about this for method-const:
> ---
> Foo const(func)(const Foo) {
>     // ...
> }
> ---
> Though I guess that may look too much like the const(Type) notation...

I have a radical notion. We could place it after the parameter list!

Foo func(const Foo) const {
    // ...
}

-- 
Kirk McDonald
http://kirkmcdonald.blogspot.com
Pyd: Connecting D and Python
http://pyd.dsource.org
July 26, 2007
Christian Kamm wrote:
> Well, the original poster basically wanted it to be possible to have
> 
> class Container(T)
> {
>   int opApply(int delegate(ref T) dg);
>   const int opApply(int delegate(ref const(T)) dg);
> }
> 
> such that foreach would work on const(Container!(T)) and Container!(T)
> alike, using the correct types. To me, that seems pretty reasonable.

Initially I thought it would overload on the 'ref' part of the delegate in opApply, eg.

class Container
{
	int[] array;
	
	//called when 'ref' not used, suitable for 'const' objects
	int opApply(int delegate(int) dg)
	{
		int result = 0;
		for (int i = 0; i < array.length; i++)
		{
			result = dg(array[i]);
			if (result) break;
		}
		return result;
	}

	//called when 'ref' used, not suitable for 'const' objects
	int opApply(int delegate(ref int) dg)
	{
		int result = 0;
		for (int i = 0; i < array.length; i++)
		{
			result = dg(array[i]);
			if (result) break;
		}
		return result;
	}
}

Current D implementation expects the delegate to have 'ref' in it, anything else is an error and foreach on a const object gives:
  Error: a.opApply can only be called on a mutable object

Instead it could, on a const object look for:
  int opApply(int delegate(Type) dg)

and on non-const:
  int opApply(int delegate(ref Type) dg)

Regan