On Thu, Jul 10, 2014 at 11:39:57AM -0700, Andrei Alexandrescu via Digitalmars-d 
wrote:
[...]
> We discussed this with Bartosz literally for weeks (him being a fan of
> auto_ptr for too long, later completely converted against it and I
> take credit for that :o)). With auto_ptr this was possible:
> 
> auto_ptr<int> a(new int);
> auto_ptr<int> b = a;
> 
> It would nullify a with copy syntax. That code won't compile with
> unique_ptr; you'd need an explicit move(a).
> 
> It only got worse from there: passing into functions, member
> variables...

Yep, if you pass auto_ptr into a function, it goes poof after the
function returns; the original reference is gone.


> MOVING WITH COPY SYNTAX DOES NOT WORK.
> 
> It's cut and dried.
[...]

I independently invented my own version of auto_ptr many years ago,
before auto_ptr got into the standard. Basically, the idea was to
annotate pointers into two classes: "owner" (i.e., auto_ptr) and
"reference" (i.e., "regular" pointer). Ownership is unique, so passing
owner pointers has destructive copy semantics (it "transfers ownership"
to the callee), whereas passing reference pointers simply makes a copy.
The rule was that copying only works one way: you can only copy an owner
pointer into a reference pointer, but never the other way around.

The thought was that somebody keeps a master reference to an object,
assuming ownership over it, whereas others may keep as many references
to it as they want, but they cannot perform owner-specific operations
(like free()). Ownership may be transferred to those who are prepared to
assume the responsibility of managing the object; such transfers involve
destructive copy (the original owner no longer holds the rights to the
object, so to speak) -- though non-owner references to that object
remain valid.

Thus, passing an owner pointer into a function that expects a reference
pointer simply makes a copy out of it (non-destructive), whereas passing
a reference pointer where an owner is expected is illegal. (Though, of
course, at the time I didn't have the means to actually enforce this, so
it was merely a convention.)

With this annotation, function signatures became more self-documenting:

        class Object { ... }

        // Argument is a reference pointer; function doesn't expect
        // ownership transfer.
        int computeValue(ref Object *obj);

        // Argument is an owner pointer; i.e., the pointer is invalid
        // after the function returns. The function will assume
        // ownership of the pointer and call free(), etc., when it's
        // done.
        void addToCache(owner Object *obj);

        int main(int argc, char *argv[]) {
                owner Object *obj = new Object; // new returns an owner pointer
                ref Object *ref = obj; // OK, makes a copy of the pointer

                obj = ref; // ILLEGAL: cannot assign owner to ref

                int x = computeValue(ref); // OK, function expects ref
                x = computeValue(obj); // OK, owner implicitly converts to ref

                addToCache(ref); // ILLEGAL: ref doesn't convert to owner

                addToCache(obj); // OK: transfers ownership of obj to function
                // N.B. from here on, obj is now invalid, and any
                // dereference is a bug until it's assigned a fresh
                // owner pointer.

                obj = new Object; // OK, create another object
                free(ref);      // ILLEGAL: can only free owner pointers
                free(obj);      // OK, transfers ownership to free
                // From here on, obj is again invalid, and any further
                // dereference is a bug.

                owner Object *obj2 = new Object;
                obj = obj2; // OK, transfers ownership to obj.
                // Now obj2 is invalid and should not be dereferenced.
        }

One thing that my system didn't address, though, was the problem of
dangling reference pointers after the corresponding owner pointers have
been freed. That remains an unsolved problem within that system, and
ultimately, requires some form of GC to adequately address.

Destructive copy, however, is tricky business; if one is not careful,
one easily ends up with invalid owner pointers in the wrong places, so I
agree with the sentiment of '=' being poor notation for destructive
copy. Having owner pointers implicitly convert to ref pointers, however,
mostly mitigates the problem -- for the most part, pointer assignments
do not involve transfer of ownership, so the usual (ref) pointer
semantics with non-destructive copy apply. When manipulating owner
pointers, you have to be extremely careful to begin with, so generally
you'd pay more attention to how it's done -- and hopefully catch
problematic assignments of owner pointers.


T

-- 
Without outlines, life would be pointless.

Reply via email to