1

According to conv.array, array-to-pointer conversion is defined like this (bold emphasis mine):

An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The temporary materialization conversion ([conv.rval]) is applied. The result is a pointer to the first element of the array

This wording seems to assume that expression, subject to the conversion, refers to an array which is implicitly understood to be the one referred to by emphasized "the array". However, this is not necessarily the case since it's legally possible, e.g., to have lvalue of type T[N] referring to object of type T:

int a[5];
auto pa = reinterpret_cast<int(*)[5]>(&a[0]);

Now, *pa is lvalue of type int[5], but refers to int object (the first element of a) since during reinterpret_cast pointer-value was unchanged due to int[5] and int being not pointer-interconvertible types. Note that, on the other hand, *std::launder(pa) is an expression of type int[5] referring to int[5] object at the address represented by pa (i.e., a), so array-to-pointer conversion would apply to it in an obvious way if needed, e.g., by subscript operator. However, applying the conversion to *pa (which applies because expression type is appropriate) results in lack of obvious interpretation of what "the array" from above wording means in this case (and, thus, what resulting pointer points to). In this answer (to question about whether T* and T(*)[N] are pointer-interconvertible), where such situation arises, it's interpreted that behaviour is undefined by omission, however as explained above the quoted paragraph does apply and defines behaviour ("The result is" provided regardless of something like "if the expression refers to array", such condition is absent from wording), but meaning of "the array" is unclear.

So, is in this case

  • behaviour indeed undefined per current wording contrary to my observations (if yes, why),
  • defined in some non-obvious way (if yes, in which way),
  • the wording is imprecise?
YurkoFlisk
  • 873
  • 9
  • 21
  • Just because something is UB doesn't mean it can't do what you expect. `reinterpret_cast(&a[0]);` compiles to but is equally as wrong. An array decays into a pointer to the first element but the reverse is not true. – Goswin von Brederlow Jul 20 '22 at 15:03
  • Note `temporary materialization conversion` in the paragraph. The array and pointer aren't identical or equivalent, one gets converted into the other. It only happens that on nearly all systems the conversion is a NOP as is the static_cast. – Goswin von Brederlow Jul 20 '22 at 15:10
  • The answer by T.C. on the question you linked, appears to cover everything you asked? (I.e. undefined by omission) – M.M Jul 21 '22 at 04:53

1 Answers1

2

The paragraph is just in general imprecise, in my opinion. It doesn't say what "the array" refers to at all. No array has been introduced before, only array types.

I guess it should probably state explicitly that it refers to the array object result of the glvalue, after temporary materialization if applicable.

Then I think it should also have a requirement that the type of that result object be similar to that of the original expression type. That way the result object will always be an array object and it can't have a "wrong" type in the same sense as for pointer arithmetic which already applies in a manual &pa[0] "decay". (see [expr.add]/6)

But that is my own interpretation of what seems a reasonable interpretation/improvement. I don't think the current wording makes that clear.


This is not the only part of the standard with imprecise wording in regard to lvalue type mismatches like this. See for example CWG 2535 for a similar situation with member access, where a similar resolution is suggested.

Language Lawyer
  • 3,378
  • 1
  • 12
  • 29
user17732522
  • 53,019
  • 2
  • 56
  • 105