decltype specifier
Since C++11
1. Value Category
- Three primary value categories: prvalue, xvalue, and lvalue.
- Two mixed categories: glvalue, value.
| Category | Definition |
|---|---|
| lvalue | Represents an object that occupies a specific location in memory |
| rvalue | Temporary value without a persistent memory address. |
| glvalue (generalized lvalue) |
Represents an expression that refers to a memory location. |
| xvalue (expiring value) |
Represents an object that is about to be moved from. It denotes an object whose resources can be reused. |
| prvalue (pure rvalue) |
Represents a temporary value that does not have a memory location. It is used to initialize objects. |
The concept of xvalue can be really confusing. Here are some common scenarios where xvalue is relevant.
1 | int&& foo() { |
Why does xvalue belong to both glvalue and rvalue?
Here are two explanations:
Resource reuse: An xvalue represents an object whose resources can be reused, typically because it is about to go out of scope or is explicitly marked for resource transfer. This makes xvalue similar to an rvalue, which represents temporary objects that can be moved or copied from.Memory location: Despite being suitable for resource transfer, an xvalue still refers to a specific location in memory, just like an lvalue. We can still take the address of an xvalue and use it to access the object’s memory.
2. decltype
The decltype specifier is used to query the type of an expression. Its syntax can be:
- decltype(entity)
- decltype(expression)
Based on the explanation in cppreference, we have two categories for using decltype.
- If the argument is an unparenthesized id-expression or an unparenthesized class member access expression, then decltype yields the type of the entity named by this expression. If there is no such entity, or if the argument names a set of overloaded functions, the program is ill-formed.
1 | int foo() { |
- If the argument is any other expression of type T, and
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.
1 | int main() { |
3. Parenthesized VS Unparenthesized
If the name of an object is parenthesized, it is treated as an ordinary lvalue expression.
- When
decltypeis used with an unparenthesized expression, it simply yields the type of the named entity. - When
decltypeis used with a parenthesized expression, it yields a type based on the value category of the expression inside the parentheses.
1 | int x = 42; |
To summarize, for parenthesized expression, it yields the type of the named entity.. For unparenthesized expression, it yields a type based on the value category of the expression inside the parentheses.
References
decltype specifier