Let me summarize my (final, I guess) proposal. I think it makes sense to compare it to C++ in order to anticipate and hopefully invalidate (mainly Andrei's) objections.

     parameter type     |   lvalue    |    rvalue
                        | C++     D   | C++     D
------------------------|-------------|------------
T                       | copy   copy | copy   move
T& / ref T              | ref    ref  | n/a    n/a
out T (D only)          |        ref  |        n/a
T&& (C++ only)          | n/a         | move
auto ref T (D only) (*) |        ref  |        ref
------------------------|-------------|------------
const T                 | copy   copy | copy   move
const T& / const ref T  | ref    ref  | ref    ref (*)
const T&& (C++ only)    | n/a         | move

(*): proposed additions

For lvalues in both C++ and D, there are 2 options: either copy the argument (pass-by-value) or pass it by ref. There's no real difference between both languages except for D's additional 'out' keyword and, with the proposed 'auto ref' syntax, an (imo negligible) ambiguity between 'ref T' and 'auto ref T' in D. Rvalues are a different topic though. There are 3 possibilites in general: copy, move and pass by ref. Copying rvalue arguments does not make sense - the argument won't be used by the caller after the invokation, so a copy is redundant and hurts performance. D corrects this design flaw of C++ (which had to introduce rvalue refs to add move semantics on top of the default copy semantics) and therefore only supports moving instead. C++ additionally supports pass-by-ref of rvalues to const refs, but not to mutable refs. I propose to allow pass-by-ref to both const (identical syntax as C++, it's perfectly safe and logical) and mutable refs (new syntax with 'auto ref' to emphasize that the parameter may be an rvalue reference, with related consequences such as potentially missing side effects).

Regarding the required overloading priorities for the proposed additions to work properly, I propose:
1) lvalues: prefer pass-by-ref
   so: ref/out T -> auto ref T (*) -> const ref T -> (const) T
   - const lvalues:   const ref T -> (const) T
- mutable lvalues: ref/out T -> auto ref T (*) -> const ref T -> (const) T 2) rvalues: prefer pass-by-value (moving: argument allocated directly on callee's stack (parameter) vs. pointer/reference indirection implied by
   pass-by-ref)
   so: (const) T -> auto ref T (*) -> const ref T (*)

Finally, regarding templates, I'm in favor of dropping the current 'auto ref' semantics and propose to simply adopt the proposed semantics for consistency and simplicity and to avoid excessive code bloating. That shouldn't break existing code I hope (unless parameters have been denoted with 'const auto ref T', which would need to be changed to 'const ref T').

Now please go ahead and shoot. :)

Reply via email to