Re: Why can't I init a new var from const var?

2017-02-11 Thread ag0aep6g via Digitalmars-d-learn

On 02/12/2017 12:15 AM, Random D user wrote:

I can init a variable from mutable source without defining any
constructor or assignment operators, but not if the source is const. I
would imagine the behavior to be the same with mutable and const source,
since it's just reading the source and copying it.


It works as long as your type has no mutable indirections (pointers, 
array, class objects). You can't just copy an indirection from const to 
mutable, because the referenced (possibly immutable) data would be 
incorrectly typed as mutable.



Is there a reason for this? Or is this a bug?
I can workaround this by making copies or casting, that just creates
ugly code everywhere.

Here's an example (with dmd 2.073):

struct Foo
{

[...]

this( const Foo source )
{
buf = source.buf.dup;
}

[...]

char[] buf;
}

Foo fun(const ref Foo foo, Foo foo2)
{
Foo bar  = foo; // Error: cannot implicitly
convert expression (foo) of type const(Foo) to Foo


Initializations like this don't call the constructor. Instead, they do 
"copy construction" and call a "postblit function" if it's defined.


See https://dlang.org/spec/struct.html#struct-postblit

So, if you want to call a function whenever your struct gets copied, you 
define a postblit function. Like so: this(this) { buf = buf.dup; }


Or if you want to do the dup-ing in specific places only, call the 
constructor explicitly: Foo bar = Foo(foo);



Foo baz  = foo2;// Ok, No need for constructors
or opAssign


Note that this does not call the constructor, and does not do the dup. 
If you want this to do the dup, postblit is the way.


[...]

//Foo bar = Foo(foo); // This works provided there is non-const
opAssign defined.
//Foo bar = cast(Foo)foo; // This seems to work as well


I wouldn't have expected the cast to call the constructor, but 
apparently it does. It's in the spec here:


https://dlang.org/spec/expression.html#CastExpression

"Casting a value v to a struct S, when value is not a struct of the same 
type, is equivalent to: S(v)"


So a cast like that is really just a fancy way of calling the constructor.

This seems a bit inconsistent, though. On postblit the spec says that 
copy construction happens with "the same type". The compiler apparently 
sees Foo and const(Foo) as the "same type" in that context. But with the 
cast, the qualifier mismatch is enough to trigger the constructor call 
which is supposed to happen when the types are not the same.



return bar;
}

Foo foo;
foo = fun(foo, foo);




Re: Why can't I init a new var from const var?

2017-02-11 Thread Ali Çehreli via Digitalmars-d-learn

On 02/11/2017 03:15 PM, Random D user wrote:
> I can init a variable from mutable source without defining any
> constructor or assignment operators, but not if the source is const. I
> would imagine the behavior to be the same with mutable and const source,
> since it's just reading the source and copying it.
>
> Is there a reason for this? Or is this a bug?
> I can workaround this by making copies or casting, that just creates
> ugly code everywhere.

Just to make sure, you don't need to define all those overloads. This is 
sufficient but you have to use a different (and idiomatic construction 
syntax.):


struct Foo
{
this( const(Foo) source )
{
buf = source.buf.dup;
}

void opAssign( const(Foo) source )
{
buf = source.buf.dup;
}

char[] buf;
}

Foo fun(const ref Foo foo, Foo foo2)
{
auto bar = Foo(foo);
// Foo bar  = foo; // Error: cannot implicitly 
convert expression (foo) of type const(Foo) to Foo
Foo baz  = foo2;// Ok, No need for constructors 
or opAssign

auto baz2 = const(Foo)(foo2);
Foo bar2;
bar2 = foo; // uses opAssing( const Foo ) / 
opAssign( const ref Foo )

Foo bar3;
bar3 = foo2;// uses opAssign( const Foo ) / 
opAssign( Foo )

Foo bar4;
bar4 = cast(const Foo)foo2; // uses opAssing( const Foo )

//Foo bar = Foo(foo); // This works provided there is non-const 
opAssign defined.

//Foo bar = cast(Foo)foo; // This seems to work as well
return bar;
}

void main() {
}

When you do this:

Foo bar = foo;

There is an implicit conversion in place, which D does not allow during 
construction. The idiomatic syntax is the following:


auto bar = Foo(foo);

The construction on the right-hand side is now explicit.

Likewise, prefer the following syntax:

auto baz2 = const(Foo)(foo2);

Notes:

When you want to treat lvalue sources differently from rvalue sources, 
you can define the following overloads as well (note const(Foo) syntax, 
which is preferred):


this( ref const(Foo) source )
{
buf = source.buf.dup;
}

void opAssign( ref const(Foo) source )
{
buf = source.buf.dup;
}

Alternatively, you can define an 'auto ref' function that automatically 
takes both lvalues and rvalues (lvalue by-ref, rvalues by-move). Note 
the empty template parentheses:


struct Foo
{
this()( auto ref const(Foo) source )
{
buf = source.buf.dup;
}

void opAssign()( auto ref const(Foo) source )
{
buf = source.buf.dup;
}

char[] buf;
}

Ali



Why can't I init a new var from const var?

2017-02-11 Thread Random D user via Digitalmars-d-learn
I can init a variable from mutable source without defining any 
constructor or assignment operators, but not if the source is 
const. I would imagine the behavior to be the same with mutable 
and const source, since it's just reading the source and copying 
it.


Is there a reason for this? Or is this a bug?
I can workaround this by making copies or casting, that just 
creates ugly code everywhere.


Here's an example (with dmd 2.073):

struct Foo
{
this( Foo source )
{
buf = source.buf.dup;
}

this( const Foo source )
{
buf = source.buf.dup;
}

this( const ref Foo source )
{
buf = source.buf.dup;
}

void opAssign( Foo source )
{
buf = source.buf.dup;
}

void opAssign( const Foo source )
{
buf = source.buf.dup;
}

void opAssign( const ref Foo source )
{
buf = source.buf.dup;
}

char[] buf;
}

Foo fun(const ref Foo foo, Foo foo2)
{
Foo bar  = foo; // Error: cannot 
implicitly convert expression (foo) of type const(Foo) to Foo
Foo baz  = foo2;// Ok, No need for 
constructors or opAssign
Foo baz2 = cast(const Foo)foo2; // Error: cannot 
implicitly convert expression (Foo(null).this(foo2)) of type 
const(Foo) to Foo

Foo bar2;
bar2 = foo; // uses opAssing( const 
Foo ) / opAssign( const ref Foo )

Foo bar3;
bar3 = foo2;// uses opAssign( const 
Foo ) / opAssign( Foo )

Foo bar4;
bar4 = cast(const Foo)foo2; // uses opAssing( const 
Foo )


//Foo bar = Foo(foo); // This works provided there is 
non-const opAssign defined.

//Foo bar = cast(Foo)foo; // This seems to work as well
return bar;
}

Foo foo;
foo = fun(foo, foo);