2015-02-15 3 views
11

У меня есть целочисленные значения, которые используются для доступа к данным в несвязанных хранилищах данных, то есть к дескрипторам. Я решил обернуть целые числа в структуре, чтобы иметь строго типизированные объекты, чтобы разные целые числа не могли быть перемешаны. Они, и должны быть, 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); 
+0

Вы хотите использовать структуры для получения сильных типов, а затем вы хотите отбросить тип, чтобы получить int. Вы получаете худшее из обоих миров. –

+0

@NeilKirk Только очень специфичные функции будут использовать raw int * массивы. Остальные будут использовать типизированные структуры. Они просто там, чтобы избежать ошибок при использовании ручек в общем случае. – rasmus

+1

Я думаю, вы должны рассказать нам больше о вашем фактическом проекте, так как ваш дизайн очень странный. –

ответ

10

reinterpret_cast<int*>(textureHandles) определенно точно так же хорошо, как &textureHandles->handle. В стандарте есть специальное исключение, унаследованное от C even, в котором говорится, что указатель на структуру стандартного макета, соответствующим образом преобразованный, указывает на начальный член этой структуры и наоборот.

Использование этого для изменения рукоятки также прекрасное. Это не нарушает правила псевдонимов, потому что вы используете lvalue типа int, чтобы изменить под-объект типа int.

Приращение результирующего указателя и использование его для доступа к другим элементам в массиве объектов Texture, но немного. Джерри Коффин уже указывал, что возможно, что sizeof(Texture) > sizeof(int). Однако даже если sizeof(Texture) == sizeof(int), арифметика указателя определяется только для указателей на массивы (где произвольный объект может рассматриваться как массив длины 1). У вас нет массива int в любом месте, поэтому добавление просто не определено.

+0

Вы уверены, что не нарушают правила псевдонимов? Что делать, если я написал члену с помощью указателя int, а затем прочитал элемент через указатель Texture? –

+1

@NeilKirk Это все еще хорошо. У вас есть lvalue типа 'Texture' для объекта типа' Texture' и lvalue типа 'int' для под-объекта типа' int'. Там нет проблемы с псевдонимом. – hvd

+0

http://stackoverflow.com/a/28529680/2068573 –

5

Нет, это не гарантируется. В частности, компилятору разрешено вставлять прокладку после любого элемента структуры, но не разрешено вставлять прокладку между элементами массива.

Тем не менее, с структуры лишь один элемент (типа int, или что-то, по крайней мере, как крупные, такие как long), шансы очень хорошо, что большинство компиляторов не будет вставлять любые отступы, поэтому ваше текущее использование является вероятно довольно безопасно как общее правило.

+0

Вы не обсуждаете строгий псевдоним, который, я думаю, будет проблемой здесь, но я не эксперт. –

+0

Для этого вопроса меня интересует только конкретный случай, когда struct содержит один элемент. Действительно ли компилятор разрешает вставлять дополнения в этом случае? – rasmus

+0

@rasmus: Да, это так. Он не может вставлять прокладку перед элементом, но может после него. –

1

Это, безусловно, нарушает строгую ступенчатость, и если функция может получить доступ к массива как через int* и Mesh* или Texture*, вы можете очень хорошо столкнуться с проблемами (хотя, возможно, только если он изменяет массив в некоторых путь).

Из вашего описания проблемы, я не думаю, что правила строгое сглаживание действительно то, что вы касаетесь. Реальная проблема заключается в том, может ли компилятор добавлять дополнения к структурам, которые не являются , присутствующими в int, так что sizeof(Mesh) > sizeof(int). И , хотя ответ формально да, я не могу представить компилятор, который бы сделал это, по крайней мере, сегодня и по крайней мере с int или больше типов в struct. (Слово на имя машины, вероятно, добавить отступы к struct, который содержал только char.)

Реальный вопрос, вероятно, более того, является ли общий код наследство, и не может быть изменена, или нет. В противном случае, очевидное решение является создание универсального типа ручки:

struct Handle 
{ 
    int handle; 
}; 

, а затем либо выводят свои типы из него, или использовать reinterpret_cast , как вы предлагаете. Существует (или, по крайней мере, была) гарантия, которая позволила получить доступ к члену struct с помощью указателя на другую структуру , если член и все предыдущие элементы были идентичны. Это, как вы имитировать наследование в C. И даже если гарантия была удалена — и единственная причина, она когда-либо присутствовал в C++ был по соображениям совместимости C — никакой компилятор не посмеет нарушить его, учитывая количество которое зависит от него. (Например, реализация Python и практически все плагины Python , в том числе написанные на C++.)

+0

Он не нарушает строгий псевдоним для доступа к объектам или подобъектам, определенным как 'int' через lvalues ​​типа' int', и я действительно не вижу, как вы можете даже сделать законный аргумент иначе. Не могли бы вы уточнить? (Гарантия, на которую вы указываете, на самом деле не гарантирует, что вы думаете, что она делает, даже в C. Она работает только в профсоюзах. Существует другая гарантия, которая здесь помогает, что указатель на структуру стандартного макета указывает на ее начальную член.) – hvd

+0

Примечание: я бы полностью согласился с вашим ответом, если бы это было наоборот. Учитывая произвольный объект 'int', попытка получить доступ к нему, как если бы это была« Ручка », не является хорошей идеей, независимо от того, находится ли она в массиве. Но об этом не спрашивает ОП. – hvd

+0

Никакая часть кода не устарела. Этот код используется для сопоставления дескрипторов данных и возможности вставлять сразу несколько дескрипторов на карту (следовательно, массивы 'int'). Но с информацией из этих удивительных ответов я, вероятно, переосмыслить эту часть дизайна. Проблема с подклассом Handle заключалась бы в том, что типизированные дескрипторы больше не будут POD. Это требование для меня. – rasmus