Settling rvalue to (const) ref parameter binding once and for all

martin kinke at libero.it
Fri Nov 9 10:05:16 PST 2012


Hi guys,

I hope you don't mind that I'm starting yet another thread about 
this tedious issue, but I think the other threads are too clogged.

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').

---

Before posting concerns about a perceived unsafety of binding 
rvalues to 'const ref' parameters, please try to find a plausible 
argument as to why the following is currently allowed:

void foo(const ref T x);
if (condition)
{
     T tmp;
     foo(tmp);
} // destruction of tmp

but the following shortcut, eliminating 3 lines (depending on 
code formatting preferences ;)) and avoiding the pollution of the 
local namespace with a 'tmp' variable, shouldn't be allowed:

if (condition)
     foo(T()); // rvalue destructed immediately after the call

---

Let me also illustrate a deterministic allocation/destruction 
scheme for the compiler implementation/language specification:

void foo(auto/const ref T a, auto/const ref T b);

foo(T(), T());
/* order:
    1) allocate argument a on caller's stack
    2) allocate argument b on caller's stack
    3) invoke foo() and pass the argument addresses (refs)
    4) destruct b
    5) destruct a
*/

I guess something like that is covered by the C++ specification 
for binding rvalues to const refs.

---

Now please go ahead and shoot. :)


More information about the Digitalmars-d mailing list