Где стандарт C ++ допускает указатели на неопределенные типы? - PullRequest
3 голосов
/ 11 февраля 2012

Где в спецификации C ++ это разрешено?Это круто.Я хочу знать, как это спецификация.Я не понял, что спецификация позволила иметь указатель на неопределенный тип.

class T;

int main(int argc, char* argv[])
{
   int x;
   T* p = reinterpret_cast<T*>(&x);
   return 0;
}

Ответы [ 4 ]

4 голосов
/ 11 февраля 2012

Единственное, что вам разрешено делать после приведения указателя к несвязанному типу (кроме char*), - это приведение к исходному типу указателя.

@ cli_hlt побил меня за предоставление раздела.

Вот само правило:

Указатель объекта может быть явно преобразован в указатель объекта другого типа. Когда значение v типа «указатель на T1» преобразуется в тип «указатель на cv T2», результатом будет static_cast<cv T2*>(static_cast<cv void*>(v)), если оба T1 и T2 являются типами стандартной компоновки (3.9), и требования к выравниванию T2 не являются более строгими, чем требования T1, или если любой тип равен void. Преобразование значения типа «указатель на T1» на тип «указатель на T2» (где T1 и T2 являются типами объектов и где требования к выравниванию T2 не являются более строгими, чем требования T1) и обратно в исходный тип возвращает исходное значение указателя. Результат любого другого такого преобразования указателя не определен.

Строгое правило псевдонимов запрещает доступ к объекту через несвязанный тип. См https://stackoverflow.com/a/7005988/103167

Еще одно правило, несколько связанное с вашим вопросом, содержится в разделе 5.4:

Операндом приведения с использованием нотации приведения может быть значение типа «указатель на неполный тип класса». Типом назначения приведения с использованием нотации приведения может быть «указатель на неполный тип класса» Если и тип операнда, и тип назначения являются типами класса и один или оба являются неполными, то не указано, является ли static_cast или используется интерпретация reinterpret_cast, даже если существуют отношения наследования между двумя классами.

4 голосов
/ 11 февраля 2012

То, что вы делаете , может быть законным, но это зависит от реального определения class T, поэтому мы не можем знать наверняка, основываясь на коде, который вы показали.

Начиная с C ++ 11 §3.9.2 / 3:

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

Эти ограничения перечислены в §3.2 / 4:

Тип класса T должен быть завершен, если:

  • определен объект типа Tили
  • объявлен элемент данных нестатического класса типа T, или
  • T используется в качестве типа объекта или типа элемента массива в new-выражение , или
  • преобразование lvalue-в-значение применяется к glvalue, ссылающемуся на объект типа T, или
  • - преобразование выражения (неявно или явно) для ввода T или
  • выражения, которое не является константой нулевого указателя и имеет тип, отличный от void*, преобразуется в type указатель на T или ссылку на T с использованием неявного преобразования, dynamic_cast или static_cast, или
  • оператор доступа к члену класса применяется к выражению типа T,или
  • оператор typeid или оператор sizeof применяется к операнду типа T, или
  • функция с типом возвращаемого значения или типом аргумента типа Tопределяется или вызывается, либо
  • определяется класс с базовым классом типа T, либо
  • присваивается lvalue типа T, либо
  • тип T является субъектом alignof выражения или
  • объявление-исключение имеет тип T, ссылку на T или указатель на T.

Шестая пуля выглядит здесь уместно, как мы видим в §5.2.10 / 7, что reinterpret_cast между типами указателей определяется в терминахиз static_cast:

Указатель объекта может быть явно преобразован в указатель объекта другого типа.Когда значение v типа «указатель на T1» преобразуется в тип «указатель на cv T2», результат равен static_cast<cv T2*>(static_cast<cv void*>(v)), если T1 и T2 являются типами стандартной компоновки итребования по выравниванию T2 не являются более строгими, чем требования T1, или если любой тип равен void.Преобразование значения типа «указатель на T1» в тип «указатель на T2» (где T1 и T2 являются типами объектов и где требования по выравниванию T2 не являются более строгими, чем требования T1) и возврат к исходному типу возвращает исходное значение указателя.Результат любого другого такого преобразования указателя не определен.

Но поскольку сначала reinterpret_cast static_cast с void*, , а затем к реальному результирующему типу указателя, чтоШестая пуля не применяется.

Итак, пока вы все еще находитесь на законной территории, несмотря на то, что T является неполным типом.Однако, если выясняется, что T не является типом стандартного макета или имеет более строгие требования к выравниванию, чем int, то последнее предложение в §5.2.10 / 7 остается верным, и вы вызываете UB.

2 голосов
/ 11 февраля 2012

Раздел 5.2.10 (7) для вашего случая (по ISO / IEC14882: 1998 (E), а также в FDIS 2011).

0 голосов
/ 11 февраля 2012

Я не уверен, что вы считаете разрешенным.Существует гарантия, что вы можете reinterpret_cast перейти от одного типа указателя к достаточно большому другому типу указателя и снова вернуться к исходному типу, и он будет исходным указателем.Спецификация для этого находится в 5.2.10 [expr.reinterpret.cast].То есть гарантированно будет работать следующее:

T* ptr = ...;
S* sptr = reinterpret_cast<S*>(ptr);
T* tptr = reinterpret_cast<T*>(sptr);
assert(ptr == tptr);

В начале этой недели у нас было внеочередное обсуждение на эту тему: если реализация Death Station 9000 (которая будет соответствующей реализацией C ++, но такжепытается нарушить пользовательский код везде, где это разрешено) XOR - битовая комбинация указателя с битовой комбинацией, случайно выбранной в начале выполнения программы, будет ли это допустимой реализацией, если типы, включенные в reinterpret_cast<T>(x)не включая char.Консенсус состоял в том, что это будет хорошо.

...