Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ idempotence of going through: pointer → reference → pointer

My understanding

  • In C++, an normal value can be converted to a pointer to itself using &value, or to a reference to itself through implicit cast.

  • As opposed to Rust, references aren't actual first-class types but merely a binary qualifier that some type declaration sites accept. References can be used transparently as if they were their referee, hiding away the internal pointer1. As such, we can use &refToValue to obtain a pointer to the referee immediately.

Question

If I convert a pointer, to a reference, back to a pointer, am I guaranteed to get the original pointer back? This would be the case in Rust, but again, Rust has first-class references defined as "normal" types abstracting over a pointer.

// Sample code by @chi
T x;
T* ptr1 = &x;
T& ref = *ptr1;
T* ptr2 = &ref;
assert(ptr1 == ptr2); // Is that always true according to the spec?

1: I know that the C++ standard doesn't require references to be implemented as pointers, but I cannot see how they could be implemented in any other way (not considering context-specific optimizations), especially if the answer to my final question is "yes".

like image 293
MrAnima Avatar asked Sep 10 '25 03:09

MrAnima


2 Answers

If I convert a pointer, to a reference, back to a pointer, am I guaranteed to get the original pointer back?

T x;
T* ptr1 = &x;
T& ref = *ptr1;
T* ptr2 = &ref;

Yes.


Full explanation on the initialisations

  • Per [expr.unary.op]/3.2, T* ptr1 = &x initializes ptr1 as a pointer to T pointing to the designated object (x).

The operand of the unary & operator shall be an lvalue of some type T. The result is a prvalue.
...
(3.2) Otherwise, the result has type “pointer to T” and points to the designated object ([intro.memory]) or function ([basic.compound]). If the operand names an explicit object member function ([dcl.fct]), the operand shall be a qualified-id.

  • Per [expr.unary.op]/1 and [dcl.ref]/5, T& ref = *ptr1 initializes ref to refer to a valid object, namely x as *ptr1 performs indirection on ptr1 and yields an lvalue denoting the object to which ptr1 points, namely x.

[expr.unary.op]/1 The unary * operator performs indirection. Its operand shall be a prvalue of type “pointer to T”, where T is an object or function type. The operator yields an lvalue of type T denoting the object or function to which the operand points.

[dcl.ref]/5 A reference shall be initialized to refer to a valid object or function.

  • Finally, per [expr.unary.op]/3.2 again, T* ptr2 = &ref initializes ptr2 as a pointer to T pointing to the designated object (x).

What about the pointer comparison?

  • As per the equality of the two pointers, I raise [basic.compound]/3.4:

A value of a pointer type that is a pointer to or past the end of an object represents the address of the first byte in memory ([intro.memory]) occupied by the object35 or the first byte in memory after the end of the storage occupied by the object, respectively.

and [expr.eq]/3.2:

[refering to the result of ==, as per defined in /1]
if the pointers are both null, both point to the same function, or both represent the same address, they compare equal.

Meaning, regardless of the actual value of ptr1 and ptr2:

ptr1 == ptr2

yields true.

like image 197
YSC Avatar answered Sep 12 '25 17:09

YSC


If I convert a pointer, to a reference, back to a pointer, am I guaranteed to get the original pointer back?

Yes.


references aren't actual first-class types ... binary qualifier that some type declaration sites accept

Indeed, while references are types, they can't serve as expression types.

So e.g. when you do &*foo, *foo isn't a reference.

like image 25
HolyBlackCat Avatar answered Sep 12 '25 18:09

HolyBlackCat