Имеет ли доступ к массиву структуры POD как массив его одного члена, нарушает строгий псевдоним?
У меня есть целочисленные значения, которые используются для доступа к данным в несвязанных хранилищах данных, то есть к дескрипторам. Я решил обернуть целые числа в структуре, чтобы иметь строго типизированные объекты, чтобы разные целые числа не могли быть перемешаны. Они, и должны быть, POD. Это то, что я использую:
struct Mesh {
int handle;
};
struct Texture {
int handle;
};
У меня есть массивы этих ручек, например: Texture* textureHandles;
.
Иногда мне нужно передать массив дескрипторов как int*
в более общие части кода. Сейчас я использую:
int* handles = &textureHandles->handle;
который по существу принимает указатель на первый элемент структуры и интерпретирует его как массив.
Мой вопрос в основном, если это законно, или если он нарушает строгий псевдоним, чтобы манипулировать int* handles
и Texture* textureHandles
, указывая на одну и ту же память. Я думаю, что это должно быть разрешено, поскольку в обоих случаях доступ к базовому типу (int
) осуществляется одинаково. Оговорка, которую я имею, связана с тем, что я обращаюсь к нескольким структурам, беря адрес члена внутри одной структуры.
Как расширение моего первого вопроса, было бы хорошо?
int* handles = reinterpret_cast<int*>(textureHandles);
Ответы
Ответ 1
reinterpret_cast<int*>(textureHandles)
определенно так же хорош, как &textureHandles->handle
. В стандарте есть исключительное исключение, унаследованное от C even, в котором говорится, что указатель на структуру стандартного макета, соответствующим образом преобразованный, указывает на начальный элемент этой структуры и наоборот.
Использование этого для изменения дескриптора также прекрасное. Это не нарушает правила псевдонимов, потому что вы используете lvalue типа int
для изменения под-объекта типа int
.
Приращение результирующего указателя и использование его для доступа к другим элементам в массиве объектов Texture
, однако, немного немного. Джерри Коффин уже указывал, что возможно, что sizeof(Texture) > sizeof(int)
. Даже если sizeof(Texture) == sizeof(int)
, хотя, арифметика указателя определяется только для указателей на массивы (где произвольный объект может рассматриваться как массив длины 1). У вас нет массива int
в любом месте, поэтому добавление просто undefined.
Ответ 2
Нет, это не гарантируется. В частности, компилятору разрешено вставлять прокладку после любого элемента структуры, но не допускается вставка прокладки между элементами массива.
Тем не менее, с конструкцией только одного элемента (типа int
или чего-то не менее значительного, например long
), вероятность того, что большинство компиляторов не будет вставлять какие-либо дополнения, текущее использование, вероятно, довольно безопасно как общее правило.
Ответ 3
Это, безусловно, нарушает строгий псевдоним, и если функция может получить доступ
массив как через int*
, так и Mesh*
или Texture*
, вы можете
очень хорошо сталкиваются с проблемами (хотя, вероятно, только в том случае, если он изменяет
массив каким-то образом).
Из вашего описания проблемы, я не думаю, что правила
строгое сглаживание - это то, что вас интересует. Реальная проблема
заключается в том, может ли компилятор добавлять дополнения к структурам, которые не являются
присутствует в int
, так что sizeof( Mesh ) > sizeof( int )
. А также
в то время как ответ формально да, я не могу представить компилятор, который
сделайте это, по крайней мере, сегодня и, по крайней мере, с int
или более крупными типами в
struct
. (Аппарат, адресованный словами, вероятно, добавит дополнение к
struct
, который содержал только char
.)
Реальный вопрос, вероятно, заключается в том, является ли общий код
наследие и не может быть изменено или нет. В противном случае очевидное решение
- создать общий тип дескриптора:
struct Handle
{
int handle;
};
а затем либо вывести из него свои типы, либо использовать reinterpret_cast
как вы предлагаете. Существует (или, по крайней мере, была) гарантия, которая позволила
доступ к элементу struct
через указатель на другой
struct, если член и все предыдущие члены были идентичны.
Вот как вы имитируете наследование на C. И даже если гарантия
был удален и единственная причина, по которой он когда-либо присутствовал в С++
был из-за совместимости с C; компилятор не осмелился нарушить
он, учитывая количество существующего программного обеспечения, которое зависит от него. (The
например, реализация Python. И практически все Python
плагины, в том числе написанные на С++.)