[C++17]
When a prvalue expression is evaluated, the standard says that it result in a value. In the case of 5
the expression is a prvalue, and it evaluates to the value 5
.
However, when you have a prvalue, mainly an initializer for an object, such as Foo{}
. What would the value of this expression be? Would the result be the temporary object created by the prvalue-to-xvalue conversion? This brings on my broader question of the difference between a value and an object.
1 the difference between objects and values using the terms “equivalent” and “identical”. Illustrate the difference using your own examples with Python lists and the “is” operator. Answer An object holds value whereas as a value is contained within an object.
Value is a property of each Object. Values are the results of operations, e.g. 1 + 1 results in an object with the value 2 .
An object that represents a descriptive aspect of the domain with no conceptual identity is called a Value Object. Value Objects are instantiated to represent elements of the design that we care about only for what they are, not who or which they are. In other words, value objects don't have their own identity.
A Value Object is an immutable type that is distinguishable only by the state of its properties. That is, unlike an Entity, which has a unique identifier and remains distinct even if its properties are otherwise identical, two Value Objects with the exact same properties can be considered equal.
The value is an abstract concept. A value is associated with a set of realizations that characterize or identifies the value. For example with a value of 10$ one could buy a book or a meal.
A value can have multiple representations. For example the 10$ can be represented by coins or stored as bits in a bank account.
An object is to a value what a bank account is to a quantity of money: the object (/the bank account) holds the representation of the value (/10$). This is described in [basic.types]:
The value representation of an object of type T is the set of bits that participate in representing a value of type T. Bits in the object representation that are not part of the value representation are padding bits.
Then in [intro.object] it is specified that an object is associated to a region of storage:
An object occupies a region of storage in its period of construction ([class.cdtor]), throughout its lifetime, and in its period of destruction ([class.cdtor]).
This distinction between the object and its value is more sensible if we consider an abstract machine with a central processor unit, that performs operations, and a separated memory where can be stored objects (that holds value representation). When an operation is performed on a value, the value is loaded into different cpu registers. So the value in the cpu does not have the same representation: a contiguous sequence of bits as it had inside the object. Moreover any cpu is free to represent the value in a register as best suit its need.
When the cpu executes an operation, it does it on a piece of value stored in a register. After that operation is performed, the cpu can save the result in memory, inside an object, or continue to operate on the value.
The decomposition of an operation in operations on values and in storages or loads from or to objects, appears in the standard:
a load is an lvalue-to-rvalue conversion [conv.lvalue] A glvalue of a non-function, non-array type T can be converted to a prvalue.
all operations in c++ will result in a sequence of fundamental operations with built-in meaning. Most of these operations applies on value (prvalue) and not on an object. Before these operations are performed lvalue-to-rvalue is applied [expr] Whenever a glvalue expression appears as an operand of an operator that expects a prvalue for that operand, the lvalue-to-rvalue,[...]
the results of these built-in operations, that operate on value, are always prvalue (a prvalue is just a value not associated to any object). Then the resulting value can be used as operand of an other built-in operation, or initialize an object (a store operation in the memory of our machine), [basic.lval]:A prvalue is an expression whose evaluation initializes an object or a bit-field, or computes the value of the operand of an operator, as specified by the context in which it appears. So in our machine representation, the act of storing the value in an object is a store.
To illustrate this, let's analyze this simple piece of code:
int main(int argc, char* argv[]){
int j = 2*argc+1;
}
2*argc
built-in operator * is called with two arguments 2
and argc
. argc
is an lvalue, so lvalue-to-rvalue is applied. The value of argc
is loaded in a cpu register (2 could be an immediate) and the multiply
operation is performed.2*argc
is a prvalue, that is directly used as the first operand (2*argc)+(argc)
. Then the resulting prvalue of this last operation is used to initialize the object j
: the resulting value is stored in the memory representation of j
.[intro.object]/1:
An object is created by a definition, by a new-expression, when implicitly changing the active member of a union, or when a temporary object is created. An object occupies a region of storage in its period of construction, throughout its lifetime, and in its period of destruction.
Regardless of whether a prvalue has class type like Foo{}
or not, as the literal 5
, is considered a value and that value is then used to initialize an object if it's really necessary, this is when the value is materialized into an object.
[class.temporary]/2:
The materialization of a temporary object is generally delayed as long as possible in order to avoid creating unnecessary temporary objects.
Under that same section you'll find a list describing when temporaries are materialized.
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