Является ли это неопределенным поведением для 'reinterpret_cast' 'T *' to 'T (*) [N]'?

Рассмотрим следующий сценарий:

std::array<int, 8> a;
auto p = reinterpret_cast<int(*)[8]>(a.data());
(*p)[0] = 42;

Это неопределенное поведение? Я думаю, что это.

  • a.data() возвращает int*, который не совпадает с int(*)[8]

  • Правила псевдонимов типа cppreference, похоже, предполагают, что reinterpret_cast недействителен

  • Как программист, я знаю, что a.data() памяти, на которую указывает a.data() представляет собой массив из 8 объектов int

Есть ли какое-либо правило, которое я пропускаю, что делает этот reinterpret_cast действительным?

Ответы

Ответ 1

Объект массива и его первый элемент не являются взаимно конвертируемыми с указателем * поэтому результат reinterpret_cast является указателем типа "указатель на массив из 8 int ", значение которого является "указателем на a[0] " 1. Другими словами, несмотря на тип, он фактически не указывает на какой-либо объект массива.

Затем код применяет преобразование массива к указателю в значение l, которое было результатом разыменования такого указателя (как часть выражения индексации (*p)[0]) 2. Это поведение конверсии указывается только тогда, когда значение lvalue фактически ссылается на объект массива 3. Так как lvalue в этом случае нет, поведение не определено отсутствием 4.


* Если возникает вопрос: "Почему объект массива и его первый элемент не являются взаимопереключателями?", Уже задан вопрос: взаимная конвертация указателя с тем же адресом.

1 См. [Expr.reinterpret.cast]/7, [conv.ptr]/2, [expr.static.cast]/13 и [basic.compound]/4.

2 См. [Basic.lval]/6, [expr.sub] и [expr.add].

3[conv.array]: "Результат - это указатель на первый элемент массива ".

4[defns.undefined]: неопределенное поведение - это "поведение, для которого этот документ не предъявляет никаких требований", в том числе "когда этот документ не содержит явного определения поведения".

Ответ 2

Да, поведение не определено.

int* (возвращаемый тип a.data()) - это другой тип из int(*)[8], поэтому вы нарушаете строгие правила псевдонимов.

Естественно, хотя (и это больше подходит для будущих читателей),

int* p = a.data();

вполне справедливо, как и последующее выражение p + n где интегральный тип n находится между 0 и 8 включительно.