Ответ 1
reinterpret_cast
ссылок
В стандарте указано, что lvalue типа T1
может быть reinterpret_cast
ссылкой на T2
, если указатель на T1
может быть reinterpret_cast
на указатель на T2
(§5.2.10/11):
Выражение lvalue типа
T1
может быть передано типу "ссылка наT2
", если выражение типа "указатель наT1
" может быть явно преобразовано в тип "указатель наT2
", используя reinterpret_cast.
Итак, нам нужно определить, можно ли преобразовать a int(*)[N]
в int(*)[I][J][K]
.
reinterpret_cast
указателей
Указатель на T1
может быть reinterpret_cast
указателем на T2
, если оба T1
и T2
являются стандартными типами макета, а T2
не имеет более строгих требований к выравниванию, чем T1
(§ 5.2.10/7):
Когда prvalue v типа "указатель на T1" преобразуется в тип "указатель на cv T2", результатом является
static_cast<cv T2*>(static_cast<cv void*>(v))
, если обаT1
иT2
являются стандартными типами макета (3.9) и требования к выравниваниюT2
не являются более строгими, чем требованияT1
, или если любой тип недействителен.
-
Являются ли
int[N]
иint[I][J][K]
стандартными макетами?int
является скалярным типом, а массивы скалярных типов считаются стандартными типами макета (§3.9/9).Скалярные типы, типы классов стандартного макета (раздел 9), массивы таких типов и cv-квалифицированные версии этих типов (3.9.3) совместно называются типами стандартного макета.
-
У
int[I][J][K]
нет более строгих требований к выравниванию, чемint[N]
.Результат оператора
alignof
дает требование согласования полного типа объекта (§3.11/2).Результат оператора
alignof
отражает требование выравнивания типа в случае полного объекта.Так как два массива здесь не являются подобъектами любого другого объекта, они являются полными объектами. Применение
alignof
к массиву дает требование выравнивания типа элемента (§5.3.6/3):Когда
alignof
применяется к типу массива, результатом будет выравнивание типа элемента.Таким образом, оба типа массива имеют одинаковое требование выравнивания.
Это делает reinterpret_cast
действительным и эквивалентным:
int (&arr3d)[I][J][K] = *reinterpret_cast<int (*)[I][J][K]>(&arr1d);
где *
и &
- встроенные операторы, которые тогда эквивалентны:
int (&arr3d)[I][J][K] = *static_cast<int (*)[I][J][K]>(static_cast<void*>(&arr1d));
static_cast
через void*
static_cast
до void*
допускается стандартными преобразованиями (§4.10/2):
Указатель типа "указатель на cv
T
", гдеT
- тип объекта, может быть преобразован в prvalue типа "указатель на cv void". Результат преобразования "указателя на cvT
" в "указатель на cv void" указывает на начало места хранения, где находится объект типаT
, как если бы объект был наиболее производным объектом (1.8 ) типаT
(т.е. не подобъект базового класса).
Затем разрешается static_cast
до int(*)[I][J][K]
(§5.2.9/13):
Значение типа "указатель на cv1
void
" может быть преобразовано в prvalue типа "указатель на cv2T
", гдеT
- тип объекта, а cv2 - это такая же cv-квалификация, как, или больше cv-квалификации, чем cv1.
Итак, отличное качество! Но можем ли мы получить доступ к объектам через ссылку на новый массив?
Доступ к элементам массива
Выполнение подтипирования массива в массиве типа arr3d[E2]
эквивалентно *((E1)+(E2))
(§5.2.1/1). Пусть рассмотрим следующий подпиттинг массива:
arr3d[3][2][1]
Во-первых, arr3d[3]
эквивалентно *((arr3d)+(3))
. Lvalue arr3d
претерпевает преобразование матрицы в указатель, чтобы дать int(*)[2][1]
. Нет требования, чтобы базовый массив должен иметь правильный тип для этого преобразования. Затем получает значение указателей (что отлично в §3.10), а затем добавляется значение 3. Эта арифметика указателя также прекрасна (§5.7/5):
Если оба операнда указателя и результат указывают на элементы одного и того же объекта массива или один за последним элементом объекта массива, оценка не должна приводить к переполнению; в противном случае поведение undefined.
Этот указатель разыменован, чтобы дать int[2][1]
. Этот процесс проходит один и тот же процесс для следующих двух индексов, что приводит к окончательному значению int
l в соответствующем индексе массива. Это значение из-за результата *
(§5.3.1/1):
Оператор унарного * выполняет косвенное обращение: выражение, к которому оно применяется, должно быть указателем на тип объекта или указателем на тип функции, а результатом является lvalue, относящееся к объекту или функции, к которой относится выражение.
Тогда отлично получается получить доступ к фактическому объекту int
через это значение lvalue, потому что lvalue имеет тип int
тоже (§3.10/10):
Если программа пытается получить доступ к сохраненному значению объекта с помощью glvalue, отличного от одного из следующих типов, поведение undefined:
- динамический тип объекта
- [...]
Так что, если я что-то пропустил. Я бы сказал, что эта программа четко определена.