www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 12672] New: make "ref" a better match than "auto_ref"

https://issues.dlang.org/show_bug.cgi?id=12672

          Issue ID: 12672
           Summary: make "ref" a better match than "auto_ref" (especially
                    for variadic arguments)
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: DMD
          Assignee: nobody puremagic.com
          Reporter: monarchdodra gmail.com

Whenever you use "auto ref", the compiler will *completely* create different
functions depending on the ref-ness of the parameters. This is necessary, since
the compiler can use exploit the pass by value/pass by ref to potentially move
things around without postblit. It also allows explicit things such as what is
in Tuple:

void opAssign(R)(auto ref R rhs)
{
    static if (!__traits(isRef, rhs))
        // Use swap-and-destroy to optimize rvalue assignment
        swap!(Tuple!Types)(this, rhs);
    else
        // Do not swap; opAssign should be called on the fields.
        field[] = rhs.field[];
}

Having two template instances is fine for trivial functions, but for long
functions that actually *do* things, it is better to use an "ref"/"value"
overload. EG:

void foo(Arg)(Arg arg)
{
    foo(arg);
}
void foo(Arg)(ref Arg arg)
{
    //longContent
}

This is *particularly* relevant for functions that take variadic arguments,
since the compiler could potentially generate "up to" 2^^N templates, that all
do the same thing (!)

The issue is that when one does:

void foo(Args...)(Args args)
{
    foo(args);
}
void foo(Args...)(ref Args args)
{
    //longContent
}

The issue with this approach though is that if *1* of the passed arguments is
not a reference, the they will *all* be passed by value (postblit and all),
which is quite wasteful.

As such, I'd want something like:

void foo(Args...)(auto ref Args args) {foo(args);}
void foo(Args...)(ref Args args)      {...}

Unfortunately, this doesn't work, since the compiler considers these to be
equal matches.

The workaround is:

//----
enum isRef(alias arg) = __traits(isRef, arg);

auto foo(Args...)(auto ref Args args)
if (!allSatisfy!(isRef, args))
{ ... }

auto foo(Args...)(ref Args args)
{ ... }
//----

But this is rather ugly and unpleasant. I think the compiler should notice that
there is an overload that takes only references, and as such, is a "better
match" than the auto-ref version.

--
Apr 28 2014