Home > other >  What does type aliasing through reference (of `signed` to `unsigned`) with `reinterpret_cast` do?
What does type aliasing through reference (of `signed` to `unsigned`) with `reinterpret_cast` do?

Time:10-18

My questions are:

  1. As of which version of the standard does the following code become valid (if any)?
  2. What is the observable behavior of the program? (In C 20)
#include <climits>

int main() {
  int foo = INT_MAX;
    reinterpret_cast<unsigned&> (foo); // foo = static_cast<int> (foo   1u)
  return INT_MIN == foo;
}

Based on the following excerpt I believe this program returns 1. (See: Example on Compiler Explorer).

7.6.1.10 Reinterpret cast [expr.reinterpret.cast]

 1) The result of the expression reinterpret_­cast<T>(v) is the result of converting the expression v to type T. If T is an lvalue reference type […] the result is an lvalue;
 7) An object pointer can be explicitly converted to an object pointer of a different type.
When a prvalue v of object pointer type is converted to the object pointer type “pointer to cv T”, the result is static_­cast<cv T*>(static_­cast<cv void*>(v)).
[Note 6: Converting a pointer of type “pointer to T1” that points to an object of type T1 to the type “pointer to T2” (where T2 is an object type and the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. — end note]
11) A glvalue of type T1, designating an object x, can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_­cast. The result is that of *reinterpret_­cast<T2 *>(p) where p is a pointer to x of type “pointer to T1”. No temporary is created, no copy is made, and no constructors ([class.ctor] or conversion functions ([class.conv]) are called.

CodePudding user response:

This has been permitted for all versions prior to C 20 according to the strict aliasing rule:

If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined:

...

  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,

C 20 changed the form of the strict aliasing rule, but the signed/unsigned variation remains permissible:

If a program attempts to access the stored value of an object through a glvalue whose type is not similar to one of the following types the behavior is undefined:

...

  • a type that is the signed or unsigned type corresponding to the dynamic type of the object

Here the dynamic type is signed int, your glvalue has the corresponding unsigned type unsigned int, so the access is allowed.

C 20 also made the result well-defined by mandating two's-complement encoding of signed numbers. Previously you would manipulate the bit representation in a well-defined way but there was no particular guarantee on what value came out when read as signed int later.

CodePudding user response:

  1. As of which version of the standard does the following code become valid (if any)?

Prolly none.

  1. What is the observable behavior of the program? (In C 20)

The behavior is undefined: reinterpret_cast<unsigned&> (foo); stores the INT_MAX 1 value into foo. Now, if you read [basic.fundamental]/1:

The range of representable values for a signed integer type is −2^{N−1} to 2^{N−1}−1 (inclusive), where N is called the width of the type.

as applying to [expr.ass]/2, then storing 2^{N-1}, which is not representable, is prolly UB.

But OK, the above reasoning may look uncertain, we assume the behavior is defined and we've stored INT_MAX 1 into an int object. Then, in return INT_MIN == foo;, the lvalue-to-rvalue conversion is applied to foo, which says:

(3.4) — the object indicated by the glvalue is read ([defns.access]), and the value contained in the object is the prvalue result.

So the result should be an int prvalue with the INT_MAX 1 value. Now, [expr.pre]/4

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

kicks in and clearly says that producing a value outside representable range is UB.

  • Related