On 02/11/2013 07:52 AM, deadalnix wrote:
Ok, We have 2 usages of ref : when you actually need to modify
informations, and for performance reasons. Let's talk about the second one.

Passing by ref to improve performance is not ideal. First this is quite
hard to know when it is actually faster to pass by ref and to pass by
value, especially in generic code. Secondly it is easy to forget to use
ref at some location, and a lot of small performance improvement are
lost in the process. Finally, this may be error prone.

I'm thinking about it for a while now and I'm now convinced that we
should allow the compiler to do that job for us. Let me explain.

When a function accept a struct, the compiler is free to use that
function, or an altered one taking a reference as parameter. Here are
some rules the compiler can use to know which one to call from callee
side :

The caller is free to call the ref version of the function unless (rules
evaluate in order) :
  - The argument is an rvalue (in such case, no postblit is executed as
well).
  - The argument is shared.
  - The argument's postblit in not pure (weakly).

The callee isn't modified for the vanilla function, but must create a
local copy of the argument in the following reasons in the ref version :
  - The argument is binded to a mutable ref.(even as hidden argument as
for member method).
  - The argument is actually modified.
  - address of anything coming from the struct is taken.

The compiler is free to apply such treatment only in a branch of the
callee, ie :

// Compiler choose to create an alternative ref version for performance.
void foo(MyStruct s) {
     if(condition) {
         // Operation require a copy to not alter what the caller see.
         // A s is copied on stack and postblit is called.
         s.field = 5;
     } else {
         // No operation requiring local copy is performed.
         // No local copy is created.
     }
}

The compiler is however disallowed to create multiple copies in the
callee. If several branches requires it, then the copy have to be made
in a common branch.

Note that the compiler don't HAVE to do this, but is allowed to.
Modifying the spec in such way allow the compiler to avoid many copies
of struct let us get rid of most ref parameters, keeping them for what
they really are for.

You can easily create a function that turns values into rvalues if it's opportune.

template isBetterToPassAsValue(T)
{
  enum isBetterToPassAsValue = ...;
}

T cheapPass(T)(ref T t) if (isBetterToPassAsValue!T)
{
  return t;
}

ref T cheapPass(T)(ref T t) if (!isBetterToPassAsValue!T)
{
  return t;
}

T cheapPass(T)(T t) if (isBetterToPassAsValue!T)
{
  return move(t);
}

void foo(T)(auto ref T t)
{
  // ...
}

void bar()
{
  S1 s1;
  foo(cheapPass(s1)); // passed by ref
  S2 s2;
  foo(cheapPass(s2)); // passed by value
}

Reply via email to