const references in C++ and D

Steven Schveighoffer schveiguy at yahoo.com
Tue Jun 1 05:21:47 PDT 2010


On Sun, 30 May 2010 19:03:57 -0400, bearophile <bearophileHUGS at 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


More information about the Digitalmars-d-learn mailing list