I'm sorry for the broadness of the question, it's just that all these details are tightly interconnected..
I've been trying to understand the difference between specifically two value categories - xvalues and prvalues, but still I'm confused.
Anyway, the mental model I tried to develop for myself for the notion of 'identity' is that the expression that has it should be guaranteed to reside in the actual program's data memory.
Like for this reason string literals are lvalues, they're guaranteed to reside in memory for the entire program run, while number literals are prvalues and could e.g. hypothetically be stored in straight asm.
The same seems to apply to std::move from prvalue literal, i.e. when calling fun(1) we would get only the parameter lvalue in the callee frame, but when calling fun(std::move(1)) the xvalue 'kind' of glvalue must be kept in the caller frame.
However this mental model doesn't work at least with temporary objects, which, as I understand, should always be created in the actual memory (e.g. if a rvalue-ref-taking func is called like fun(MyClass()) with a prvalue argument). So I guess this mental model is wrong.
So what would be the correct way to think about the 'identity' property of xvalues? I've read that with identity I can compare addresses but if I could compare addresses of 2 MyClass().members (xvalue according to the cppreference), let's say by passing them by rvalue refs into some comparison function, then I don't understand why I can't do the same with 2 MyClass()s (prvalue)?
One more source that's connected to this is the answer here:
What are move semantics?
Note that even though std::move(a) is an rvalue, its evaluation does not create a temporary object. This conundrum forced the committee to introduce a third value category. Something that can be bound to an rvalue reference, even though it is not an rvalue in the traditional sense, is called an xvalue (eXpiring value).
But this seems to have nothing to do with 'can compare addresses' and a) I don't see how this is different from the 'traditional sense' of the rvalue; b) I don't understand why such a reason would require a new value category in the language (well, OK, this allows to provide dynamic typing for objects in OO sense, but xvalues don't only refer to objects).
I personally have another mental model which doesn't deal directly with identity and memory and whatnot.
prvalue comes from "pure rvalue" while xvalue comes from "expiring value" and is this information I use in my mental model:
Pure rvalue refers to an object that is a temporary in the "pure sense": an expression for which the compiler can tell with absolute certainty that its evaluation is an object that is a temporary that has just been created and that is immediately expiring (unless we intervene to prolong it's lifetime by reference binding). The object was created during the evaluation of the expression and it will die according to the rules of the "mother expression".
By contrast, an expiring value is a expression that evaluates to a reference to an object that is promised to expire soon. That is it gives you a promise that you can do whatever you want to this object because it will be destroyed next anyway. But you don't know when this object was created, or when it is supposed to be destroyed. You just know that you "intercepted" it as it is just about to die.
In practice:
struct X;
auto foo() -> X;
X x = foo();
^~~~~
in this example evaluating foo() will result in a prvalue. Just by looking at this expression you know that this object was created as part of the return of foo and will be destroyed at the end of this full expression. Because you know all of these things you can prologue it's lifetime:
const X& rx = foo();
now the object returned by foo has it's lifetime prolongued to the lifetime of rx
auto bar() -> X&&
X x = bar();
^~~~
In this example evaluating bar() will result in a xvalue. bar promises you that is giving you an object that is about to expire, but you don't know when this object was created. It can be created way before the call to bar (as a temporary or not) and then bar gives you an rvalue reference to it. The advantage is you know you can do whatever you want with it because it won't be used afterwords (e.g. you can move from it). But you don't know when this object is supposed to be destroyed. As such you cannot extend it's lifetime - because you don't know what its original lifetime is in the first place:
const X& rx = bar();
this won't extend the lifetime.
When calling a func(T&& t) the caller is saying "there's a t here" and also "I don't care what you do to it". C++ does not specify the nature of "here".
On a platform where reference parameters are implemented as addresses, this means there must be an object present somewhere. On that platform identity == address. However this is not a requirement of the language, but of the platform calling convention.
A platform could implement references simply by arranging for the objects to be enregistered in a particular manner in both the caller and callee. Here an identity could be "register edi".
Related
I thought I've managed to fully understand (with the help of other SO questions, thanks) the C++17's change regarding value categories, but now I've noticed this problem which suggests I don't really understand them.
In C++11, there was a "has identity / can be moved from" interpretation of value categories and the definition of what "identity" means is still present in cppreference:
has identity: it's possible to determine whether the expression refers to the same entity as another expression, such as by comparing addresses of the objects or the functions they identify (obtained directly or indirectly).
In C++17, "has identity / can be moved from" is no longer true, but the new definition is also based on the concept of "identity":
a glvalue (“generalized” lvalue) is an expression whose evaluation determines the identity of an object, bit-field, or function;
My question/misunderstanding is: is this the same meaning of "identity", or is it a different "identity"? The way I understand it, the situation in c++17 is as follows:
A f() { return A(); }
A a = f(); // 1: f() is a prvalue expression, used to directly initialize a.
f(); // 2: f() is a prvalue expr., converted to xvalue by temporary materialization
A&& r = f(); // 3: f() is a prvalue expr., converted to xvalue by temporary materialization
In the 2nd and 3rd case, I obtain an xvalue, which means it should have an identity. So I should be able to get its address [edited:] I should be able to determine whether it refers to the same entity as some other expression, but I don't think I can. Of course, in the 3rd case I'm able to do "&r" as a separate command and then compare its address to the other expression's address, but that's because A&& is an lvalue. Is taking the address via A&& what they mean in this case by "obtained directly or indirectly"? I assumed this can't be the correct answer, because in C++11 I can also easily do
A&& r = f(); // 4: f() is a prvalue expression. Temporary materialization does
// not happen, the temporary (prvalue) gets its lifetime
// extended to match the lifetime of the reference
&r;
and yet the object referenced by r is still a temporary despite having its lifetime extended, and in c++11 temporaries are prvalues. So I assume just the fact I can bind A&& to it (of which, like any lvalue, I can take an address but only in a separate expression) is not enough to conclude that its evaluation determines an identity (after all, whatever happens in the line "&r;" is not part of an evaluation of my original expression), and yet in c++17 it looks to me like this would be the only possible interpretation?
Can you help me by determining which part of what I wrote is incorrect? Or is it correct, and the answer is simply that the word "identity" has changed its meaning?
Why do you think that the C++11 concepts no longer apply? The page says that that version introduced the “has identity” idea, not that it’s the only version that uses it. What C++17 did was say that prvalues “wait until” they are used for initialization of an object to become an object at all. (It therefore merges “being an object” with “having identity”, which is simpler; plainly every object can have this be detected.)
What that really means is that the address of that target object is invisibly passed to the site of the prvalue construction so that it appears in the correct place from the start. Compilers were already doing that under the RVO name, but the change guaranteed that and removed formal movability restrictions.
The xvalue certainly has an identity: if you pass a prvalue to a function by (either kind of) reference, it can take its address. It can even return it so that (during the same full-expression) the function that created it can use its address. The prohibition on taking the address of a temporary is a separate safety measure that applies even after (say) using static_cast<A&&>(get_a()) to force temporary materialization. (However, it can’t stop you from taking the address of a glvalue that refers to the temporary returned from another function.)
I've been questioning myself whether Temporary Objects do have identity. I know that the following is valid:
object.temporary_object().modify()
as far as the object returned is non-const or the function called on the object do not modify immutable members.
According to the value categories definition, prvalues results can be moved but have no identity and as Temporary Objects are result of prvalue expressions, how can they be modified?
The linked document is not normative. In some sense it seems to describe what prvalues ought to be, rather than what they were at the time. In C++17, it became true that prvalues have no identity---but in C++11 and C++14, it was not quite so.
In C++11 and C++14, a prvalue of class type does have an identity, because, as you've observed, it's possible to call a method on it, and there are also ways to observe its address. Similarly, prvalues of array type have identity. Prvalues of scalar type (e.g., integer literals) do not have identity. Binding them to references will cause the materialization of a temporary object, which now has an address but is no longer observable as a prvalue.
In C++17, prvalues have no identity, and are not temporary objects, but are instead expressions that can be used to create temporary (or non-temporary) objects. Moving from a prvalue to an object effectively "invokes" the prvalue. A temporary object is only observable as an xvalue.
You can modify the temporary object just like any other. The modifications will just be moot as they are thrown away when the life-time of the temporary object ends and it is destructed.
It's kind of similar to something like this:
SomeClass object;
// Some code...
{
// Entering a new scope, the life-time of variables in here ends when the scope ends
SomeOtherClass temporary_object = object.temporary_object();
temporary_object.modify();
// Now the scope ends, and the life-time of temporary_object with it
}
// Here there exists no such things as "temporary_object"
All modifications you made to temporary_objects will be lost when the nested scope ends, and temporary_object is destructed.
One important disclaimer: You can design the SomeOtherClass (from the above example) to keep a link (reference or pointer) to object, and the modify() function can use that link to modify object itself. Those modifications will still exist after temporary_object is destructed, as they are modification on object itself instead of temporary_object.
rvalue references: what exactly are "temporary" objects, what is their scope, and where are they stored?
Reading some articles, rvalues are always defined as "temporary" objects like Animal(), where Animal is a class, or some literal e.g. 10.
However, what is the formal definition of rvalues/"temporary" objects?
Is new Animal() also considered a "temporary" object? Or is it only values on the stack, like Animal() and literals stored in code?
Also, where are these "temporary" objects stored, what is their scope, and how long are rvalue references to these values valid?
Firstly it is important not to conflate the terms "rvalue" and "temporary object". They have very different meanings.
Temporary objects do not have a storage duration. Instead, they have lifetime rules that are specific to temporary objects. These can be found in section [class.temporary] of the C++ Standard; there is a summary on cppreference, which also includes a list of which expressions create temporary objects.
In practice I'd expect that a compiler would either optimize the object out, or store it in the same location as automatic objects are stored.
Note that "temporary object" only refers to objects of class type. The equivalent for built-in types are called values. (Not "temporary values"). In fact the term "values" includes both values of built-in type, and temporary objects.
A "value" is a completely separate idea to prvalue, xvalue, rvalue. The similarity in spelling is unfortunate.
Values don't have scope. Scope is a property of a name. In many cases the scope of a name coincides with the lifetime of the object or value it names, but not always.
The terms rvalue, lvalue etc. are value categories of an expression. These describe expressions, not values or objects.
Every expression has a value category. Also, every expression has a value, except expressions of void type. These are two different things. (The value of an expression has a non-reference type.)
An expression of value category rvalue may designate a temporary object, or a non-temporary object, or a value of built-in type.
The expressions which create a temporary object all have value category prvalue, however it is then possible to form expressions with category lvalue which designate that same temporary object. For example:
const std::string &v = std::string("hello");
In this case v is an lvalue, but it designates a temporary object. The lifetime of this temporary object matches the lifetime of v, as described in the earlier cppreference link.
Link to further reading about value categories
An rvalue reference is a reference that can only bind to an expression of value category rvalue. (This includes prvalue and xvalue). The word rvalue in its name refers to what it binds to, not its own value category.
All named references in fact have category lvalue. Once bound, there is no difference in behaviour between an rvalue reference and an lvalue reference.
std::string&& rref = std::string("hello");
rref has value category lvalue , and it designates a temporary object. This example is very similar to the previous one, except the temporary object is non-const this time.
Another example:
std::string s1("hello");
std::string&& rref = std::move(s1);
std::string& lref = s1;
In this case, rref is an lvalue, and it designates a non-temporary object. Further, lref and rref (and even s1!) are all indistinguishable from hereon in, except for specifically the result of decltype.
There are two different things to concern about. First of all, there's the language's point of view. Language specifications, such as the C++ standard(s), don't talk about things such as CPU registers, cache coherence, stacks (in the assembly sense), etc... Then, there's a real machine's point of view. Instruction set architectures (ISAs), such as the one(s) defined by Intel manuals, do concern about this stuff. This is, of course, because of portability and abstraction. There's no good reason for C++ to depend on x86-specific details, but a lot of bad ones. I mean, imagine if HelloWorld.cpp would only compile for your specific Core i7 model for no good reason at all! At the same time, you need CPU specific stuff sometimes. For instance, how would you issue a CLI instruction in a portable way? We have different languages because we need to solve different tasks, and we have different ISAs because we need different means to solve them. There's a good reason explaining why your smartphone doesn't use an Intel CPU, or why the Linux kernel is written in C and not, ahem... Brainfuck.
Now, from the language's point of view, a "rvalue" is a temporary value whose lifetime ends at the expression it is evaluated in.
In practice, rvalues are implemented the same way as automatic variables, that is, by storing their value on the stack, or a register if the compiler sees it fit. In the case of an automatic variable, the compiler can't store it in a register if its address is taken somewhere in the program, because registers have no "address". However, if its address is never taken, and no volatile stuff is involved, then the compiler's optimizer can place that variable into a register for optimization's sake. For rvalues, this is always the case, as you can't take a rvalue's address. From the language's point of view, they don't have one (Note: I'm using oldish C terminology here; see the comments for details, as there are way too many C++11 pitfalls to annotate here). This is necessary for some things to work properly. For instance, cdecl requires that small values be returned in the EAX register. Thus, all function calls must evaluate into a rvalue (consider references as pointers for simplicity's sake), because you can't take a register's address, as they don't have one!
There's also the concept of "lifetime". From the language's perspective, once some object's lifetime "ends", it ceases to be, period. When does it "begins" and "ends" depends on the object's allocation means:
For objects with dynamic storage, their lifetime sexplicitly start by means of new expressions and explicitly end by means of delete statements. This mechanism allows them to survive their original scope (e.g: return new int;).
For objects with automatic storage, their lifetimes start when their scope is reached in the program flow, and end when their scope is exited.
For objects with static storage, their lifetimes start before main() is called and end once main() exits.
For objects with thread-local storage, their lifetimes start when their respective thread starts, and end when their respective thread exits.
Construction and destruction are respectively involved in an object's lifetime "start" and "end".
From a real machine's point of view, bits are just bits, period. There are no "objects" but bits and bytes in memory cells and CPU registers. For things like an int, that is, a POD type, "ending its lifetime" translates into doing nothing at all. For non-trivially destructible non-POD types, a destructor must be called at the right moment. However, the memory/register that once contained the "object" is still there. It just happens that it can now be reused by something else.
Is new Animal() also considered a "temporary" object? Or is it only values on the stack, like Animal() and literals stored in code?
new Animal() allocates memory in the heap for an Animal, constructs it, and the whole expression evaluates into a Animal*. Such an expression is an rvalue itself, as you can't say something like &(new Animal()). However, the expression evaluates into a pointer, no? Such a pointer points to an lvalue, as you can say things such as &(*(new Animal())) (will leak, though). I mean, if there's a pointer containing its address, it has an address, no?
Also, where are these "temporary" objects stored, what is their scope, and how long are rvalue references to these values valid?
As explained above, a "temporary object"'s scope is that of the expression that encloses it. For example, in the expression a(b * c) (assuming a is a function taking a rvalue reference as its single argument), b * c is an rvalue whose scope ends after the expression enclosing it, that is, A(...), is evaluated. After that, all remaining rvalue references to it that the function a may have somehow created out of its parameter are dangling and will cause your program to do funny things. In order words, as long as you don't abuse std::move or do other voodoo with rvalues, rvalue references are valid in the circumstances that you'ld expect them to be.
Why don't rvalues have a memory address? Are they not loaded into the RAM when the program executes or does it refer to the values stored in processor registers?
Your question ("Why don't rvalues have a memory address?") is a bit confused. An rvalue is a kind of expression. Expressions don't have addresses: objects have addresses. It would be more correct to ask "why can one not apply the address-of operator to an rvalue expression?"
The answer to that is rather simple: you can only take the address of an object and not all rvalue expressions refer to objects (for example, the expression 42 has a value but does not refer to an object).
Some rvalue expressions do refer to objects, but such objects lack persistence. An object referred to by an rvalue expression is a temporary object and is destroyed at the end of the expression in which it is created. Such objects do indeed have addresses (you can easily discover this by calling a member function on a temporary object; the this pointer must point to the temporary object and thus the temporary object must have an address).
This is the fundamental difference between lvalue expressions and rvalue expressions. Lvalue expressions refer to objects that have persistence: the object to which an lvalue expression refers persists beyond a single expression.
Think of rvalue as value of an expression. The value itself doesn't have address. But the objects involve in the expression do have address. You can take address of an object, even be it a temporary object.
Consider this,
const int & i = 10; //ok
Here, 10 is an rvalue, so it appears that &i is an address of the 10. No, that is wrong. &i is an address of the temporary object of type int, which is created out of the expression 10. And since the temporary object cannot be bound to non-const reference, I use const. That means, the following is an error:
int & i = 10; //error
The question mixes two different aspects related to the "specification" and to the "implementation".
The "specification" define some abstract rules that defines how the language behave respect to an abstract machine it works with.
Adapt that "abstract machine" to the "real one" under it its a purpose of the compiler (not the language).
What the specification is enforcing is that -by the language stand point- a "storage" (a piece of memory with a proper address) is given only to objects that have a name (for the existence of the scope that name lives) or that are dynamically allocated with an explicit request (new).
Everything else is "temporary": assign, copy and moves like an object, but is not required to exist in a well defined and stable place. At least, not for the purpose of the language.
Of course, that has to stay (physically) somewhere, so you can -with appropriate casting or conversion- trying to guess a memory address. But the language specification does not grant any consistent behavior if you try to actively use it.
That means that different compilers can behave differently, and optimize the better they can respect to the real machine they target.
What do you mean, rvalues do have an address. Ever tried
Type const value& = rvalue;
Type const* address = &value;
Simply take this case
int a = 1 + 2;
1+2 gets resolved to 3.
Ask yourself:
Is 3 an object?
Where would 3 be located in memory?
When you need the address of an object, you use &.
If rvalues would be addressable, it means you could declare a pointer to where your computer decided to store 3
int* a = &3;
Does that seem right? :)
I know that the code written below is illegal
void doSomething(std::string *s){}
int main()
{
doSomething(&std::string("Hello World"));
return 0;
}
The reason is that we are not allowed to take the address of a temporary object. But my question is WHY?
Let us consider the following code
class empty{};
int main()
{
empty x = empty(); //most compilers would elide the temporary
return 0;
}
The accepted answer here mentions
"usually the compiler consider the temporary and the copy constructed as two objects that are located in the exact same location of memory and avoid the copy."
According to the statement it can be concluded that the temporary was present in some memory location( hence its address could have been taken) and the compiler decided to eliminate the temporary by creating an in-place object at the same location where the temporary was present.
Does this contradict the fact that the address of a temporary cannot be taken?
I would also like to know how is return value optimization implemented. Can someone provide a link or an article related to RVO implementation?
&std::string("Hello World")
The problem with this isn't that std::string("Hello World") yields a temporary object. The problem is that the expression std::string("Hello World") is an rvalue expression that refers to a temporary object.
You cannot take the address of an rvalue because not all rvalues have addresses (and not all rvalues are objects). Consider the following:
42
This is an integer literal, which is a primary expression and an rvalue. It is not an object, and it (likely) does not have an address. &42 is nonsensical.
Yes, an rvalue may refer to an object, as is the case in your first example. The problem is that not all rvalues refer to objects.
Long answer:
[...] it can be concluded that the temporary was present in some memory location
By definition:
"temporary" stands for: temporary object
an object occupies a region of storage
all objects have an address
So it doesn't take a very elaborate proof to show that a temporary has an address. This is by definition.
OTOH, you are not just fetching the address, you are using the builtin address-of operator. The specification of the builtin address-of operator says that you must have a lvalue:
&std::string() is ill-formed because std::string() is a rvalue. At runtime, this evaluation of this expression creates a temporary object as a side-effect, and the expression yield a rvalue that refers to the object created.
&(std::string() = "Hello World") is well-formed because std::string() = "Hello World" is a lvalue. By definition, a lvalue refers to an object. The object this lvalue refers to is the exact same temporary
Short answer:
This is the rule. It doesn't need the (incorrect, unsound) justifications some people are making up.
$5.3.1/2 - "The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualifiedid."
Expressions such as
99
A() // where A is a user defined class with an accessible
// and unambiguous default constructor
are all Rvalues.
$3.10/2 - "An lvalue refers to an
object or function. Some rvalue
expressions—those of class or
cv-qualified class type—also refer to
objects.47)"
And this is my guess: Even though Rvalues may occupy storage (e.g in case of objects), C++ standard does not allow taking their address to maintain uniformity with the built-in types
Here's something interesting though:
void f(const double &dbl){
cout << &dbl;
}
int main(){
f(42);
}
The expression '42' is an Rvalue which is bound to the 'reference to const double' and hence it creates a temporary object of type double. The address of this temporary can be taken inside the function 'f'. But note that inside 'f' this is not really a temporary or a Rvalue. The moment it is given a name such as 'dbl', it is treated as an Lvalue expression inside 'f'.
Here's something on NRVO (similar)
A temporary is an example of a C++ "rvalue." It is supposed to purely represent a value within its type. For example, if you write 42 in two different places in your program, the instances of 42 are indistinguishable despite probably being in different locations at different times. The reason you can't take the address is that you need to do something to specify that there should be an address, because otherwise the concept of an address is semantically unclean and unintuitive.
The language requirement that you "do something" is somewhat arbitrary, but it makes C++ programs cleaner. It would suck if people made a habit of taking addresses of temporaries. The notion of an address is intimately bound with the notion of a lifetime, so it makes sense to make "instantaneous" values lack addresses. Still, if you are careful, you can acquire an address and use it within the lifetime that the standard does allow.
There are some fallacies in the other answers here:
"You cannot take the address of an rvalue because not all rvalues have addresses." — Not all lvalues have addresses either. A typical local variable of type int which participates in a simple loop and is subsequently unused will likely be assigned a register but no stack location. No memory location means no address. The compiler will assign it a memory location if you take its address, though. The same is true of rvalues, which may be bound to const references. The "address of 42" may be acquired as such:
int const *fortytwo_p = & static_cast<int const &>( 42 );
Of course, the address is invalid after the ; because temporaries are temporary, and this is likely to generate extra instructions as the machine may pointlessly store 42 onto the stack.
It's worth mentioning that C++0x cleans up the concepts by defining the prvalue to be the value of the expression, independent of storage, and the glvalue to be the storage location independent of its contents. This was probably the intent of the C++03 standard in the first place.
"Then you could modify the temporary, which is pointless." — Actually temporaries with side effects are useful to modify. Consider this:
if ( istringstream( "42" ) >> my_int )
This is a nice idiom for converting a number and checking that the conversion succeeded. It involves creating a temporary, calling a mutating function on it, and then destroying it. Far from pointless.
It can be taken, but once the temporary ceases to exist, you have a dangling pointer left.
EDIT
For the downvoters:
const std::string &s = std::string("h");
&s;
is legal. s is a reference to a temporary. Hence, a temporary object's address can be taken.
EDIT2
Bound references are aliases to what they are bound to. Hence, a reference to a temporary is another name for that temporary. Hence, the second statement in the paragraph above holds.
OP's question is about temporaries (in terms of the words he uses), and his example is about rvalues. These are two distinct concepts.
One reason is that your example would give the method write access to the temporary, which is pointless.
The citation you provided isn't about this situation, it is a specific optimization that is permitted in declarators with initializers.
Why is taking the address of a temporary illegal?
The scope of temporary variables are limited to some particular method or some block, as soon as the method call returns the temporary variables are removed from the memory, so if we return the address of a variable which no longer exists in the memory it does not make sense. Still the address is valid but that address may now contain some garbage value.