Reputation: 337
In C++ it is impossible to bind an r-value argument to a non-const
l-value reference. But I noticed that when I call on the r-value object the method returning *this
it compiles somehow.
Example: Obliviously this snippet od code will not compile
class X
{
};
void bar(X&)
{
}
int main()
{
foo(X{}); // invalid initialization of non-const reference of type 'X&' from an rvalue of type 'X'
}
But adding a simple method to X
makes it compilable:
class X
{
public:
X& foo() { return *this; }
};
void bar(X&)
{
}
int main()
{
bar(X{}.foo());
}
Why does it work? Does it mean that after calling foo
r-value object becomes l-value object? Is it safe to use such construction? Is there any other way to achieve similar effect without creating a new method (something like X.this
)?
Upvotes: 2
Views: 88
Reputation: 28416
As mentioned in a comment, foo
returns an lvalue, not an rvalue, so passing it to bar
is just fine.
If it feels strange that X{}.foo()
returns an lvalue even if X{}
itself is an rvalue, think of foo()
as a function which (implicitly) takes this
and returns something (which happens to be what this
points to) by lvalue reference. Now, how does foo
take this
? Does it accept rvalue? Does it accept lvalues? Yes to both, but you could write things so as to allow only one usage (look for ref-qualified member functions here):
class X
{
public:
X& fool() & { return *this; } // called on lvalue objects
X& foor() && { return *this; } // called on rvalue objects
};
void bar(X&)
{
}
int main()
{
X x;
bar(x.fool());
//bar(x.foor()); // fails
//bar(X{}.fool()); // fails
bar(X{}.foor());
}
However, pay attention to what you do:
#include <iostream>
#include <type_traits>
class X
{
public:
X& fool() & { return *this; }
X& foor() && { return *this; }
~X() { std::cout << "dtor\n"; }
};
void bar(X&)
{
}
int main()
{
auto& xxx = X{}.foor();
// use xxx, but X{} has been destroyed
std::cout << "bye\n";
}
Upvotes: 3