Code
struct CustomReal
{
private real value;
this(real value)
{
this.value = value;
}
CustomReal opBinary(string op)(CustomReal rhs) if (op == "+")
{
return CustomReal(value + rhs.value);
}
bool opEquals(ref const CustomReal x) const
{
return value == x.value; // just for fun
}
}
// Returns rvalue
CustomReal Create()
{
return CustomReal(123.123456);
}
void main()
{
CustomReal a = Create();
assert(a == CustomReal(123.123456)); // OK. CustomReal is temporary but lvalue
assert(a == Create()); // Compilation error (can't bind to rvalue)
assert(a != a + a); // Compilation error (can't bind to rvalue)
}
Compilation error
prog.d(31): Error: function prog.CustomReal.opEquals (ref const const(CustomReal) x) const is not callable using argument types (CustomReal)
prog.d(31): Error: Create() is not an lvalue
http://ideone.com/O8wFc
Questions:
const ref can't bind to rvalue? Is it ok?ref CustomReal or const ref CustomReal from opBinary() to fix this problem? Is it ok?ref CustomReal Create() { return CustomReal(0.0); }
The only difference between ref and const ref is that const ref is const and ref is not. Both must take a variable. Neither can take a temporary. This is different from C++ where const T& will take any value of type T - including temporaries.
opBinary cannot return either ref or const ref, because there's no variable to return. It's creating a temporary. The same goes for Create. And creating a local variable with the value that you want to return doesn't help either, because you can't return a reference to a local variable. It would end up referring to a variable which didn't exist anymore.
What you need to do here is add another overload of opEquals:
bool opEquals(CustomReal x) const
{
return value == x.value; // just for fun
}
With that, your code will compile.
I would point out though that the current situation with opEquals does need to be ironed out a bit. You'll notice that if you only have the overload for opEquals that I gave you and not the one that you currently have, the code fails to compile, and you get an error similar to this:
prog.d(15): Error: function prog.CustomReal.opEquals type signature should be const bool(ref const(CustomReal)) not const bool(CustomReal x)
The compiler is currently overly picky about the exact signature of opEquals for structs (a few other functions - such as toString - have similar issues). It's a known issue and will probably be resolved in the near future. However, for now, just declare both overloads of opEquals. If you compare a CustomReal with a variable, then the const ref version will be used, and if you compare a CustomReal with a temporary, then the other version will be used. But if you have both, you should be okay.
Now, why
assert(a == CustomReal(123.123456));
works, and
assert(a == Create());
doesn't, I'm not sure. I'd actually expect both of them to fail, given that const ref can't take a temporary, but for some reason, the compiler accepts it here - it probably has to do with how it treats opEquals special. Regardless, as I said, there are some issues with opEquals and structs which need to be ironed out, and hopefully that'll happen soon. But in the meantime, declaring both overloads of opEquals seems to do the trick.
EDIT: It appears that the reason that
assert(a == CustomReal(123.123456));
works, and
assert(a == Create());
doesn't is because of the fact (for reasons that I don't understand) a struct literal is considered an lvalue, whereas the return value of a function which is not ref is (unsurprisingly) an rvalue. There are a couple of bug reports related to it, arguing that struct literals should be rvalues, but apparently they're lvalues by design (which baffles me). In any case, that's why a function which takes const ref works with a struct literal but not with the return value of a function.
#1: Yep, const ref can't bind to rvalues. Andrei thinks it was a bad idea to allow that in C++ IIRC.
http://digitalmars.com/d/archives/digitalmars/D/const_ref_rvalues_103509.html#N103514
The weird thing is struct literals count as lvalues in D2, so this works:
struct A {}
void foo(ref A a) {}
void main()
{
foo(A());
}
while calling the following doesn't:
static A bar()
{
return A();
}
foo(bar());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With