I. CONSTEXPR and constant expressions
Constant expressions (const expression) are expressions that do not change the value and can be evaluated in the compilation process. Obviously, literals are constant expressions, and const objects initialized with constant expressions are also constant expressions.
Whether an object (or an expression) is a constant expression is determined by its data type and initial value, for example:
- const int max_files = 20; Max_files is a constant expression
- const int limit = max_files + 1; Limit is a constant expression
- int staff_size = 27; Staff_size is not a constant expression
- const int sz = get_size (); SZ is not a constant expression
Although the initial value of staff_size is a literal constant, it does not belong to a constant expression because its data type is just a normal int, not a const int. On the other hand, although SZ itself is a constant, its specific value is not available until run time, so it is not a constant expression.
In a complex system, it is difficult (almost certainly not) to tell whether an initial value is a constant expression. Of course, you can define a const variable and set its initial value to one of the constant expressions we think of, but when it is actually used, the initial value is often found and the expression is very variable, although it is required. It can be said that in such cases, the definition and use of objects are two things.
C++11 The new standard specifies that a variable is allowed to be declared as a constexpr type so that the compiler can verify that the value of the variable is a constant expression . A variable declared as constexpr must be a constant and be initialized with a constant expression:
- constexpr int MF = 20; 20 is a constant expression
- constexpr int limit = MF + 1; MF + 1 is a constant expression
- constexpr int sz = size (); The correct declaration statement is only when size is a constexpr function
Although you cannot use a normal function as the initial value of a constexpr variable, the new standard allows you to define a special constexpr function. This function should be simple enough so that the results can be computed at compile time so that the constexpr variable can be initialized with the CONSTEXPR function.
In general, if you assume that a variable is a constant expression, declare it as a constexpr type.
The value of a constant expression needs to be evaluated at compile time, so the type used to declare constexpr must be limited. Because these types are generally simpler, and the values are obvious and easy to get, they are referred to as "literal type" (literal type).
In the data types that have been touched so far, arithmetic types, references, and pointers all belong to the literal type. Custom classes Sales_item, IO libraries, and string types are not literal types and cannot be defined as constexpr.
Although pointers and references can be defined as constexpr, their initial values are strictly limited. The initial value of a constexpr pointer must be nullptr or 0, or an object stored in a fixed address.
It is worth mentioning that the variables defined in the function body are generally not stored in fixed addresses, so the constexpr pointer cannot point to such variables. Conversely, objects defined outside of all function bodies have fixed addresses that can be used to initialize the constexpr pointer. Similarly, a function is allowed to define a class of variables that are valid beyond the function itself (that is, local static variables), which have fixed addresses as well as variables defined outside the body of the function. Therefore, the constexpr reference can be bound to such a variable, and the constexpr pointer can also point to such a variable.
Pointers and constexpr
It must be made clear that if a pointer is defined in the CONSTEXPR declaration, the qualifier constexpr is only valid for pointers, regardless of the object that the pointer refers to:
- const int *p = nullptr; P is a pointer to an integral type constant
- constexpr int *q = nullptr; Q is a constant pointer to an integer
The types of P and Q are far apart, p is a pointer to a constant, and q is a constant pointer, where the key is to constexpr the object it defines to the top-level const.
Similar to other constant pointers, the constexpr pointer can either point to a constant or to a very good amount:
- constexpr int *NP = nullptr; NP is a constant pointer to an integer whose value is null
- int j = 0;
- constexpr int i = 42; Type of I is an integral type constant
- Both I and J must be defined outside the function body
- constexpr Const int *p = &i; P is a constant pointer, pointing to the integer constant I
- constexpr int *P1 = &j; P1 is a constant pointer, pointing to the integer J
Two. Left and right values
The C + + expression is either an rvalue or an lvalue. These two nouns are inherited from the C language, originally to help the memory: the left value can be located on the left side of the assignment statement, the right value is not.
In the C + + language, the difference between the two is much more complex.
An lvalue expression evaluates to an object or a function, whereas some lvalue represented by a constant object cannot actually be the left-hand operand of an assignment statement . In addition, although some expressions are evaluated as objects, they are rvalue rather than lvalue values.
A simple generalization can be made: When an object is used as an rvalue, the object's value (content) is used, and when the object is used as an lvalue, the object's identity (in-memory position) is used.
A classic summary of what you see: the formal distinction of lvalue rvalue (or syntax distinction) is the ability to use the address & operator; The semantic distinction (that is, its essence) is whether the expression represents a persisted object or a temporary object.
When using the keyword Decltype, the left and right values are also different. If the evaluation result of an expression is an lvalue, decltype acts on the expression (not the variable) and gets a reference type.
For example, suppose the type of P is int*, because the dereference operator generates an lvalue, so the result of Decltype (*p) is int&p, on the other hand, because the fetch address operation Fuzhou to the right value, so the result of Decltype (&P) is
int**, which means a pointer to an integer pointer.
For left and right values in C + +, refer to Links: Left and Right values in C + +, a summary of Lvalue Rvalue, left and right values in C++11, Lvalue, Rvalue, and rvalue references.
Here is my own understanding of Lvalue and rvalue: strictly speaking, the left and right values have a very strict criterion, that is, whether the address & operator can be used.
On the left and right side of the equals sign (the left value can appear on the left or right side of the equal sign, and the rvalue can only appear on the right) is imperfect, for example, some lvalue is not available to the left of the assignment, typically a constant variable object: such as the const int i = 1, the variable i in addition to the initialization process is not allowed to re-assign value, but I is undoubtedly an lvalue, you can use the address character.
Literal constants that are not related to objects (only built-in types have) are generally rvalue, but one exception is the string literal, which can take an address, which is an lvalue, but cannot be on the left side of the equals sign.
In contrast, sometimes the right value can be on the left side of the equals sign, although the new standard does not advocate this. For example, string s1, S2; S1 + s2 = "wow!".
S1 + s2 is the right value, but is located to the left of the assignment symbol. To maintain backward compatibility, the new standard library still allows assigning values to the right value.
Three. Rvalue reference
Left and right values in C + +