I'm trying to come up a simple example for an operation that results in a rvalue.
This test case should have worked, but surprisingly (to me), the result of adding two int
s is not an rvalue (reference). What am I missing here?
void test(int i, int j)
{
// this assert should pass, but fails:
static_assert(std::is_same<decltype(i + j), int&&>(), "i + j should be a rvalue");
// this assert passed, but should fail:
static_assert(std::is_same<decltype(i + j), int>(), "this assert should fail...");
}
i + j
is a prvalue expression,
A prvalue ("pure rvalue") expression is an expression that does not have identity and can be moved from.
a + b, a % b, a & b, a << b, and all other built-in arithmetic expressions;
not a xvalue,
An xvalue ("expiring value") expression is an expression that has identity and can be moved from.
And decltype specifier yields T
for prvalue, not T&&
.
a) if the value category of expression is xvalue, then decltype yields T&&;
b) if the value category of expression is lvalue, then decltype yields T&;
c) if the value category of expression is prvalue, then decltype yields T.
You can make it xvalue by std::move
:
static_assert(std::is_same<decltype(std::move(i + j)), int&&>(), "std::move(i + j) is a xvalue then this static_assert won't fail");
Based on @songyuanyao's Answer, I noticed that my mistake was checking the wrong thing: My intention was to check whether the result of i+j
would bind to a rvalue reference, but I checked whether it is a rvalue reference.
decltype deduces the type based on the value category, not based on what reference type the value would bind to:
1) if the value category of expression is
xvalue
, then decltype yieldsT&&
;
2) if the value category of expression islvalue
, then decltype yieldsT&
;
3) if the value category of expression isprvalue
, then decltype yieldsT
.
As shown in the list, since C++11, rvalues
don't exist as a distinct category on the lowest level. They are now a composite category containing both prvalues
as well as xvalues
. The question as written asks whether the expression is a rvalue reference
and checks whether it is an xvalue
.
From the list above, it is clear that i+j
is a prvalue
, so the third case applies. This explains why decltype(i + j)
is int
and not int&&
. Both xvalues
and prvalues
bind to rvalue references.
So by checking for whether i+j
binds to a lvalue reference
or a rvalue reference
confirms that, indeed, it binds to a rvalue reference
:
void foo(const int& f)
{
std::cout << "binds to lvalue reference" << std::endl;
}
void foo(int&& f)
{
std::cout << "binds to rvalue reference" << std::endl;
}
void test(int i, int j)
{
foo(i); // lvalue -> lvalue ref
foo(std::move(i)); // xvalue -> rvalue ref
// (std::move converts the argument to a rvalue reference and returns it as an xvalue)
foo(i + j); // prvalue -> rvalue ref
}
In conclusion: i+j
is not a rvalue reference, but it binds to one.
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