On Thursday, 15 November 2012 at 17:24:44 UTC, Jonathan M Davis
wrote:
On Thursday, November 15, 2012 14:21:41 Dan wrote:
I do not see how copy constructors can help. Receiving const(T)
or const(T) ref to anything and hoping to copy it is the
challenge and doing it from a copy constructor is no easier
than
a postblit.
Doing it from a postblit is impossible.
I understand and this makes sense. postblit is not a deep copy,
it is only what you choose to make it.
Doing it from a copy constructor _is_
possible (assuming that they're added to the language - they
don't help you
any until they are).
I question the need for this - at least for structs composed of
structs, dynamic arrays and associative arrays. Classes are
another issue.
The core problem with postblit constructor is that it
does a shallow copy _before_ entering the postblit constructor.
This seems
great at first, because then you only have to copy stuff in the
postblit itself
which actually needs a deep copy. But it fails completely with
const and
immutable, because it's illegal to alter anything which is
const or immutable
- even by casting them to mutable temporarily to make the
change (doing so is
undefined behavior). So, you can't change _anything_ in a const
postblit
constructor, meaning that you can't make a deep copy of
anything.
Understood. I don't think the problem with postblit is what it
does _before_ I do my thing. The problem for this case is it can
not guarantee what I do *in* my postblit and therefore it does
not and can not know I'm leaving with no sharing. I don't see
this as a failure, though. You want a copy, then you need to make
a copy. Find a way other than postblit. The suggestion is use
copy constructors instead. I don't see how this will help - but I
have not found the proposal (do you have a link?). I have read
DIP10 which was an early attempt to get at this stuff - but I
think it did not go far.
A copy constructor, on the other hand, does not do any copying
beforehand. It
all must still be constructed. The copy constructor then
constructs the new
object with deep copies of the data. This works with const and
immutable,
because it's construction rather than mutation. The postblit
constructor fails
because it requires you to use mutation, which doesn't work
with const.
A copy ctor does not copy anything beforehand. True. Then the
idea with this new feature is to leave it to you to copy the
fields from inside your new copy ctor. But you will no more be
able to copy them from a copy ctor than you were from a postblit
because you have a const instance as your source and something
extra is needed for each field. Now if that something extra is
more copy constructors all down the line - I think that would
work but is unnecessary. I think that the real need is a global
dup or deep copy function. It should be possible without touching
our structs and I think I have something fairly close when it
comes to structs, associative arrays, and dynamic arrays.
So, as nice an idea as it is, the postblit constructor is a
failure.
I feel it is not a failure. It is doing its job just fine. The
natural tendency, especially coming from c++ is to assume const
values assigned into non-const lvalues should be ok - because we
are used to deep copy semantics just working. The only problem is
copy construction (and therefore postblit) is not the right tool
to copy const instances with reference semantics. I think a
global dup function would be the right tool and it is doable,
hopefully with a reasonable performance overhead.
The issue is you want to copy a const(T). Suppose I give you a
function that works like this (by the way this is working code (I
use 2.061))
import std.stdio;
import opmix.mix;
struct S {
char[] c;
}
void foo(ref const(S) s) {
// I really need a copy here
auto copy = s.gdup;
assert(s.c.ptr != copy.c.ptr);
writeln(copy);
}
void main() {
S s = {['a','b','c']};
foo(s);
}
Now if you had this gdup, all your copy ctors would look like
this:
struct S {
char[] c;
S(ref const(S) s) {
swap(this, s.gdup);
}
}
But if you had that gdup in the first place, there would be no
need for copy ctors. Besides, if the compiler could *know* that
my new copy ctor was really doing a deep copy - why make me write
it in the first place. Just provide the implementation for me.
Allowing me to do anything in the copy ctor allows me to
introduce sharing which is forbidden.
Does this make sense?
Thanks
Dan