I'm trying to figure out why the following snippet calls the LValue cast operator overload:
#include <iostream>
class Foo
{
public:
Foo(int i = 0) : i(i) {}
operator const int& () const &
{
std::cout << "lvalue\n";
return i;
}
operator int () const &&
{
std::cout << "rvalue\n";
return i;
}
int i = 0;
};
Foo Fool()
{
return Foo(5);
}
int main()
{
const int& i = Fool();
const int j = Fool();
return 0;
}
The current outputs are:
lvalue
rvalue
But from my understanding Fool() returns an rvalue and since const& can bind to rvalues there is no need to construct an lvalue Foo.
Can anyone explain why lvalue is being constructed? I believe this is a dangling lvalue.
Okay, so the thing to note here is that overload resolution only ever considers one conversion function for
i. They don't both participate, and so the reference qualifier cannot be used to differentiate them. For the case of binding a referenceAccording to the text in bold, when initializing
i, our only candidate isoperator int const&. So overload resolution can either pass here, or fail entirely. But it cannot selectoperator int, since that one is not even under consideration. It succeeds because a const qualified lvalue reference can bind to the object argument.On the other hand, for initializing a value
So when initializing
jboth conversion functions participate as overloads, and here the reference qualifier makes a difference.You do get a dangling reference here, and it seems to be due to a dark corner in the language. The bullet in the first quoted paragraph could probably be refined to consider the binding of const lvlaue references better. Since those may bind to temporaries as well, your second conversion operator could ideally be a candidate under better rules.