# [C++] Your Rvalue Isn't Meant to Be the Rvalue
###### tags: `C++` `Compiler` `Parser` `Software`
A small talk about the *rvalue reference*, which itself is an *lvalue* that refers to a *rvalue* object. Intriguingly, *rvalue reference* can't be used as an *rvalue* before explicitly casting to *rvalue*, or *rvalue reference*.
:::info
For example, why compiling the following code will get a `cannot bind rvalue reference of type ‘int&&’ to lvalue of type ‘int’` error?
```cpp=
void f(int&& i) {}
void ff(int&& i) { f(i); } // rvalue reference would become an lvalue
// should be f((int &&) i) or f(std::move(i))
```
What's more (What's worse), why a template function with a `T&&` (something like an "rvalue reference") argument, can accept an lvalue? What are forwarding references for?
```cpp=
template<typename T>
void f(T&& t) {}
template<typename T>
void ff(T&& t) { f(t); } // rvalue reference would become an lvalue
// should be f((T&&) t) or f(std::forward<T>(t))
int main() {
int i;
f(i); // the lvalue argument (int) becomes lvalue reference (int&)
// so this is legal, what???
}
```
(Detailed answer is discussed in the [Rvalue Reference Question](#Rvalue-Reference-Question) below.)
:::
## TL;DR
Since C++11, new expression reference type categories have been introduced:

> C\++98 value category: ==**lvalue**== & ==**rvalue**==.
> C++11 just subdivides ==**rvalue**== into: **xvalue (`T&&`)** + **prvalue (`T&`)**.
:::info
**Add new ==move semantics== (xvalue) to the expression reference type.**
- **lvalue (`T`):** for object basic assignment.
- **rvalue:**
- **prvalue (normal rvalue `T&`):** for copy semantics assignment.
- **xvalue (unamed rvalue reference `T&&`):** for move semantics assignment.
:::
## C++ Value Categories
*"One of the properties" of the expressions in C++: expression value types, expression reference types, ...*
For example, what are the value/reference types of these expressions:
```cpp=
int (i); // expression is lvalue
(i); // expression is lvalue
(++i); // expression is lvalue
((int &) i); // expression is lvalue
(i++); // expression is prvalue
template<int (i)> // expression is prvalue
(42); // expression is prvalue
([](){}); // expression is prvalue
((int &&) i); // expression is xvalue
(std::move(i)); // expression is xvalue
```
> This is evaluated on type checking of semantic analysis performed by the parser.
- [Value categories - cppreference](https://en.cppreference.com/w/cpp/language/value_category.html)
- [What are rvalues, lvalues, xvalues, glvalues, and prvalues? - StackOverflow](https://stackoverflow.com/questions/3601602/what-are-rvalues-lvalues-xvalues-glvalues-and-prvalues)
- [Reference declaration - cppreference.com](https://en.cppreference.com/w/cpp/language/reference)
### Reference Types
*Expression E* can be one of the following reference type:
1. **Lvalue (left/locator-value)** ⟺ *expression E* refers to an entity referencing to an *identity (e.g., address, name, alias)* that makes it accessible outside of *E*.
```cpp
int int_var_locator;
```
- **Modifiable Lvalue:** Can't have an array type, an incomplete type, or a type with the const attribute or with const members.
> For example, if ptr is a pointer to a storage region, then *ptr is a modifiable l-value that designates the storage region to which ptr points.
2. **Xvalue (expiring-value)** ⟺ any of the following *expression E*:
- The **function** that returns *rvalue reference*.
```cpp
int&& f();
```
- The **cast** to *rvalue reference* which refers to object's actual value entity.
```cpp
(int &&) 7;
```
```cpp
static_cast<int&&>(7); // equivalent to the above one
```
```cpp
std::move(7); // equivalent to the above one
```
- The **pointer-to(-xvalue)-member expression** such as `<xvalue>.*member_ptr` will yield an xvalue as well.
```cpp
struct A {
std::string s;
int i = 10;
void func() {}
} a;
// void (A::*f_ptr)() = &A::func;
// int A::*i_ptr = &A::i;
/* i_ptr: pointer to the member A::i of struct A
*
* like C++ version of C offsetof(A, i) for struct:
* (size_t)((char*)&((A*)(0))->i - (char*)0)
*
* equivalent to (int)(char *) &A::i
* (though C++ not allowed pointer-to-member casting to raw pointer,
* making this casting UB,
* kinda member-access abstraction.)
*/
auto s_ptr = &A::s;
std::string s = std::move(a).*s_ptr;
// here, (<xvalue>.*s_ptr) will yield an xvalue.
```
Refs:
- [What is a pointer to class data member "::*" and what is its use? - StackOverflow](https://stackoverflow.com/questions/670734/what-is-a-pointer-to-class-data-member-and-what-is-its-use)
- [offsetof(3) — Linux manual page](https://man7.org/linux/man-pages/man3/offsetof.3.html)
- The **accessing class member expression** of non-static data member of non-reference type that is *lvalue*.
```cpp
struct A {
int i;
};
A&& f() {
return A();
}
f().i; // 1. f() is lvalue
// 2. A::i is a non-static data member of non-reference type
//
// => f().i is xvalue, an access expression to A::i
```
3. **PRvalue (pure-rvalue)** ⟺ *expression E* belongs neither to *lvalue* nor to *xvalue* (i.e. non-locatable & movable temporaries in expressions or literals).
```cpp
a + b;
```
### Conceptual Reference Type Categories
- **Rvalue (right-value)** ⟺ *E* refers to an ==movable *floating* entity== stored at some address in memory (i.e. *literal values*) that hasn't had any identity that makes it accessible outside of *E* "yet".
- [Binding - MDN Web Docs](https://developer.mozilla.org/en-US/docs/Glossary/Binding)
- **GLvalue (generalized-lvalue)** ⟺ *E* refers to an ==named *representative* entity== that has an *identity (e.g., address, name, alias)*.
| is *GLvalue* (↓) \ *Rvalue* (→) ? | yes (**Can be moved**) | no (**Cannot be moved**) |
|:---------------------------------:|:----------------------:|:------------------------:|
| yes (**Has identity**) | *Xvalue* | *Lvalue* |
| no (**No identity**) | *PRvalue* | N/A |

## Rvalue Reference Question
Ok, with all this prerequisite background knowledge. Let's move back to the original question:
Why the following C++ code causes `the "rvalue" can't bind to the "lvalue"` error:
```cpp=
void f(int&& i) {}
void ff(int&& i) { f(i); }
```
but this one doesn't:
```cpp=
void f(int&& i) {}
void ff(int&& i) { f((int&&) i); }
```
We can get the answer at [std::move - cppreference.com](https://en.cppreference.com/w/cpp/utility/move), but I think it is more vivid to see the below exellent explanation organized by ChatGPT:
### First Case
- `int&&` means that f accepts an rvalue reference.
- In `ff`, the parameter `i` is of type `int&&`, but within the function body, `i` is an lvalue. This is because `i` has a name and can be referred to multiple times.
When you call `f(i)`, you are trying to pass an lvalue (`i`) to a function that expects an rvalue reference. This is why you get a compilation error: an rvalue reference cannot bind to an lvalue.
### Second Case
- In this version, you are explicitly casting `i` to an rvalue reference using `static_cast<int&&>(i)` (the C-style cast `(int&&)i` achieves the same effect).
- This cast tells the compiler to treat `i` as an rvalue, allowing it to bind to the rvalue reference parameter of `f`.
### Modern C++ Solution
In modern C++, it's recommended to use `std::move` for such casts because it makes the intention clearer:
```cpp=
#include <utility> // for std::move
void f(int&& i) {}
void ff(int&& i) { f(std::move(i)); }
```
`std::move` effectively performs a `static_cast` to an rvalue reference, signaling that `i` can be "moved from ...", which is often what you want when dealing with rvalue references.
### Summary
- **lvalues** have a name and a persistent state.
- **rvalues** are temporary and typically do not have a persistent state.
- **rvalue references** (`T&&`) bind to rvalues.
- **lvalues** cannot be passed directly to functions expecting rvalue references.
The casting (`static_cast<int&&>(i)` or `std::move(i)`) converts an lvalue (that originally was an rvalue) to an rvalue, enabling binding to rvalue references.
but, wait ...
:::warning
**[Template Auto Argument Type Deduction] Forwarding References (Universal References)**
*Perfect Forwarding (e.g., preserving `volatile const int &`) & Reference Collapsing Rules*
- [Template argument deduction - cppreference](https://en.cppreference.com/w/cpp/language/template_argument_deduction.html#Other_contexts)
- [Reference declaration - cppreference](https://en.cppreference.com/w/cpp/language/reference.html): Reference collapsing
```cpp=
template<typename T>
void ff(T&& t) { // forwarding reference
t.f(); // rvalue reference (lvalue) or lvalue reference (lvalue)
((T&&) t).f(); // forwarding reference (NOT rvalue reference)
// equivalant to
(std::forward<T>(t)).f();
}
int main() {
struct S {
void f() & { std::cout << "&\n"; }
void f() && { std::cout << "&&\n"; }
} s;
ff(s);
ff((S&&) s);
}
```
with `decltype(auto)` for returned values with perfect forwarding (auto type deduction):
```cpp=
template<typename T, typename... Args>
decltype(auto) init(T t, Args&&... args) {
return t { std::forward<Args>(args)... };
}
```
with in-struct template auto argument type deduction:
```cpp=
template<typename T>
struct A {
template<typename F>
A(T&&, F&&); // T&&: rvalue reference
// F&&: forwarding reference
};
```
`T&&` arguments with `const` (i.e., not cv-unqualified):
```cpp=
template<typename T>
void f(const T&&); // const T&&: rvalue reference
```
The `T&&` ++(cv-unqualified) parameters++ in a template function are considered as a *forwarding references* for perfect forwarding semantics, **NOT rvalue references anymore**:
- if `T` is an rvalue: `T&&` would collapse to an rvalue reference (`T&&`).
- if `T` is an lvalue: `T&&` would collapse to an lvalue reference (`T&`).
> Also, using template's forwarding reference means nothing is passed by value!
> (Even `int i` will implicitly cast to `int&`!)
:::
:::info
What if I just want template functions with rvalue reference arguments?
> Use SFINAE test!
```cpp=
template<typename T>
requires(std::is_rvalue_reference_v<T &&>)
void func(T&&) {}
```
or `delete`
:arrow_right: For more details, see: [Modern C++ Programming - shibarashinu](https://hackmd.io/@shibarashinu/H1i8HFH0A).
:::
## Dangling Reference
However, such complex reference type mechanisms in C++ sometimes may cause dangerous dangling references due to bad access to invalid, out-of-lifetime references (e.g, out of scope, abiding by RAII rules), leading programs to undefined behavior.
```cpp=
std::string& f()
{
std::string s = "Example";
return s; // exits the scope of s,
// its destructor is called and its storage deallocated
}
std::string& r = f(); // dangling reference
std::cout << r; // undefined behavior: reads from a dangling ref
auto s = f(); // undefined behavior: copy-initializes from a dangling ref
```
```cpp=
std::vector<std::string> vec;
std::string str = "example";
vec.push_back(std::move(str)); // since here, str's value is unspecified
std::cout << str; // undefined behavior: reads from a value-moved variable
```