c++c++11language-lawyerc++17value-categories

Is cppreference using the term "[Object's] identity" is two different meanings for c++11 and for c++17?


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?


Solution

  • 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.)