Thread overview
Re: Confused about const
Mar 20, 2010
Paul D. Anderson
Mar 20, 2010
Paul D. Anderson
Mar 20, 2010
bearophile
Mar 20, 2010
Paul D. Anderson
March 20, 2010
bearophile Wrote:

> Paul D. Anderson:
> > My struct has a dynamic array as a member -- that seems to be the problem. This code doesn't compile:
> > 
> > struct S {
> >  int x;
> >  int[] a;
> > }
> > 
> > S foo(const S b) {
> >     S other = b;
> >     return other;
> > }
> > void main() {}
> 
> This compiles, oh joy:
> 
> struct S {
>     int x;
>     int[] a;
> }
> S foo(const S b) {
>     S other;
>     other.x = b.x;
>     other.a = b.a.dup; // the dup is necessary here
>     return other;
> }
> void main() {}
> 
> 
> The idea now is to find a way to encapsulate that manual copying & dupping into some overloaded operator of S. But I have failed so far. Operator overloading in D2 is not an easy thing, it needs training. (That's why I have recently asked for the compiler to be strict to avoid wrong usages of the operator overloading.)
> 
> Bye,
> bearophile

Yes, thanks to you and biozic for finding the root of the problem. Here's a piece of code that demonstrates the problem:

//-------------

struct S {
int x;
int[] a;

// including this postblit causes an error because the default // constructor it applies to doesn't allow const argument.

/+this(this) {
	a = a.dup;
}+/

// adding a const to the argument of this constructor allows case 2.
this(const S s) {
	x = s.x;
	a = s.a.dup;
}

// including or excluding this constructor doesn't change
// the compilability.
/+this( S s) {
	x = s.x;
	a = s.a.dup;
}+/

// an opAssign with a const argument allows case 3.
S opAssign(const S s) {
	this.x = s.x;
	this.a = s.a.dup;
	return this;
}

// including this doesn't make a difference.
// I think this is the same as the default opAssign.
/+S opAssign(S s) {
	this.x = s.x;
	this.a = s.a.dup;
	return this;
}+/

}

// case 1: no const. According to the spec this is
// a copy constructor in action.
S foo(S b) {
    S other = b;
    return other;
}

// case 2: const, but assignment requires the creation of a
// non-const S from before assignment.
S bar(const S b) {
    S other = S(b);
    return other;
}

// case 3: const, but creation and assignment are separate.
// this is a default construction and opAssign(const).
S foo(const S b) {
    S other;
	other = b;
    return other;
}

// case 4:const. According to the spec this is
// a copy constructor in action, but it goes awry.
// does not compile.
S foo(const S b) {
    S other = b;
    return other;
}

void main() {}

/----------------------

So I understand better now, thanks, what is wrong. I'm a little disappointed that there is apparently no way to implement case 4 for any struct with a reference.

Paul

p.s. Does it make any sense to send this over to the D forum to get more eyes on the problem?


March 20, 2010
After further review, I now realize that the right way (for me) to do this is to add a .dup property. So instead of

S foo(const S s) {
    S other;
    other = s;
}

or

S foo(const S s) {
    S other = S(s);
    return other;
}

which didn't look right to me, I can write

S foo(const S s) {
    S other = s.dup;
    return other;
}

which is ultimately an equivalent operation.

Thanks again, everyone for taking the time to explain what was going on.

Paul

It just looks better to me.





Paul D. Anderson Wrote:

> bearophile Wrote:
> 
> > The idea now is to find a way to encapsulate that manual copying & dupping into some overloaded operator of S. But I have failed so far. Operator overloading in D2 is not an easy thing, it needs training. (That's why I have recently asked for the compiler to be strict to avoid wrong usages of the operator overloading.)
> > 
> > Bye,
> > bearophile
> 
> 
> So I understand better now, thanks, what is wrong. I'm a little disappointed that there is apparently no way to implement case 4 for any struct with a reference.
> 
> Paul

March 20, 2010
Paul D. Anderson:

> After further review, I now realize that the right way (for me) to do this is to add a .dup property.<

Steven Schveighoffer has given you quite good answers.
A dup is generally not enough, because what you dup can have other immutable references nested inside. Dup is not a deep copy.
The summary of this topic is (until inout works) that if you can return a const value, then don't copy things. This is the preferred option, because one of the main purposes of const data is to not have to copy them.
If you can't return a const, then don't mark the input values as const. The safety given by the not deep const of Java is only partial safety.

Bye,
bearophile
March 20, 2010
bearophile Wrote:

> Paul D. Anderson:
> 
> > After further review, I now realize that the right way (for me) to do this is to add a .dup property.<
> 
> Steven Schveighoffer has given you quite good answers.
> A dup is generally not enough, because what you dup can have other immutable references nested inside. Dup is not a deep copy.
> The summary of this topic is (until inout works) that if you can return a const value, then don't copy things. This is the preferred option, because one of the main purposes of const data is to not have to copy them.
> If you can't return a const, then don't mark the input values as const. The safety given by the not deep const of Java is only partial safety.
> 
> Bye,
> bearophile

You're right that dup itself can have problems with pointers/references. My struct doesn't have 2nd-level references, so dup works fine for me.

Paul