Does that access to a hypothetical array element definitely UB?

Viewed 118
int arr[2];
int b = arr[2];

According to [expr.sub#1]

The expression E1[E2] is identical (by definition) to *((E1)+(E2)), except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise.

Where (E1)+(E2) is determined by [expr.add#4.2]

When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.

  • [...]
  • Otherwise, if P points to an array element i of an array object x with n elements ([dcl.array]), the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) array element i + j of x if 0 ≤ i + j ≤ n and the expression P - J points to the (possibly-hypothetical) array element i - j of x if 0 ≤ i - j ≤ n
  • Otherwise, the behavior is undefined.

In this example, arr + 2 points to the hypothetical array element and there is no UB here. The result of *(arr+2) is a glvalue that refers to the object pointed to by arr + 2. In the current standard, there is no normative rule that explicitly says indirection of the pointer would result in UB, as well as [basic.life]. Hence, I think there are two interpretations for this example. One is that there is an actual object of type int created in that storage, in this case, int b = arr[2]; is well-defined. The other interpretation is that there's is no object or an object of another type in that storage, according to [basic.lval#11]

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:

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

In this case, the example is judged to be UB. So, I wonder that Is this example definitely UB or the same as what I analyzed(namely, an unspecified case)?

1 Answers

In this example, arr + 2 points to the hypothetical array element and there is no UB here. The result of *(arr+2) is a glvalue that refers to the object pointed to by arr + 2.

You're skipping a step here. The hypothetical array element is hypothetical. That means it does not actually exist. But in the next sentence, you're talking about "the object pointed to by arr + 2". There is no object at that address.

The bit you quote from [basic.lval#11] is about aliasing - where an object does exist. It does not apply to hypothetical objects.

To clarify a misunderstanding from one of your comments, it should be noted that in C++ it is possible for memory to exist without an object existing at that address. The whole point of placement new is to create an object in memory where one didn't exist before. So you can't assume that there must be some sort of object at arr+2.

Related