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