Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the result of "decltype(i+j)" not an rvalue reference?

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 ints 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...");
}
like image 928
anderas Avatar asked Apr 21 '16 07:04

anderas


2 Answers

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"); 
like image 133
songyuanyao Avatar answered Oct 18 '22 01:10

songyuanyao


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 yields T&&;
2) if the value category of expression is lvalue, then decltype yields T&;
3) if the value category of expression is prvalue, then decltype yields T.

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 xvaluesand 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.

like image 35
anderas Avatar answered Oct 18 '22 02:10

anderas