On Sun, 30 May 2010 19:03:57 -0400, bearophile <bearophileh...@lycos.com> wrote:

I am translating a small C++ program to D2 and I am having problems. I am learning, but my mind is not large enough for all the subtleties of both C++ and D2 yet :-)

This C++ program compiles:


struct Vec {
    void operator+(const Vec& other) {}
};
Vec bar(Vec x) {
    return x;
}
int main() {
    Vec() + bar(Vec());
}



I think this is the equivalent D2 program:

// program #1
struct Vec {
    void opBinary(string Op:"+")(const ref Vec other) {}
}
Vec bar(Vec x) {
    return x;
}
void main() {
    Vec() + bar(Vec()); // line 9
}


But DMD gives me:
temp3.d(9): Error: function temp3.Vec.opBinary!("+").opBinary (ref const const(Vec) other) is not callable using argument types (Vec)
temp3.d(9): Error: bar((Vec())) is not an lvalue


I vaguely remember a discussion about this in the D newsgroup, that D acts like this on purpose, so I think this is not a D bug. What is the right way to translate that C++ code to D2? (So far I have just commented out the 'ref' in opBinary).


Another case, to me it seems the same problem (D2 code):


// program #2
struct Vec {
    Vec opOpAssign(string Op:"+=")(ref Vec other) {
        return this;
    }
    Vec opBinary(string Op:"*")(int k) {
        return this;
    }
}
void main() {
    Vec x;
    x += Vec() * 2; // line 12
}


DMD prints:
temp3.d(12): Error: function temp3.Vec.opOpAssign!("+=").opOpAssign (ref Vec other) is not callable using argument types (Vec)
temp3.d(12): Error: (Vec()).opBinary(2) is not an lvalue

Again I have just commented out the 'ref' here and in most other operator overloading methods.


I have seen that in some cases I can use "auto ref" (like in those two examples), but not in all of them, I don't know why. For example here:


// program #3
struct Vec {
    Vec opOpAssign(string Op)(auto ref Vec other) if (Op == "+=") {
        return this;
    }
    Vec opBinary(string Op:"+")(Vec other) {
        Vec result;
        return result += other;
    }
}
void main() {
    Vec v;
    v += Vec() + Vec(); // line 13
}


DMD prints:
temp3.d(13): Error: function temp3.Vec.opOpAssign!("+=").opOpAssign (auto ref Vec other) is not callable using argument types (Vec)
temp3.d(13): Error: (Vec()).opBinary((Vec())) is not an lvalue

Do you know why "auto ref" isn't right here?

I've had a long private discussion with Andrei about this problem. C++ allows rvalues to be passed into functions that accept const ref. However, it surprisingly is less optimal for rvalues to pass by ref. The reason is because, you must put the temporary on the stack, and then also put the reference on the stack. By simply passing the struct without ref, it's not copied as an lvalue would be, it's simply used where it is, because the compiler knows that it's no longer needed in the calling function. So the optimizer can work more efficiently to remove those extra pushes and pops.

In C++, the optimial solution would be to allow the following duplicated methods:

foo(const ref Vec);
foo(const Vec);

as overloads, with the compiler choosing the first for lvalues and the second for rvalues. But you cannot overload based on ref, so this is not allowed.

D's solution is to use auto ref, but I think your attempts to use it show that it doesn't work. You should file a bug with your program 3.

-Steve

Reply via email to