Широко распространено мнение о том, что пробивание типов через reinterpret_cast
каким-то образом запрещено (правильно: «неопределенное поведение», то есть « поведение, для которого настоящий международный стандарт не предъявляет никаких требований », сявное примечание, что реализации могут определять поведение) в C ++.Неправильно ли я использую следующие рассуждения, чтобы не согласиться, и, если да, то почему ?
[expr.reinterpret.cast] / 11 говорится:
Выражение glvalue типа T1
может быть приведено к типу «ссылка на T2
», если выражение типа «указатель на T1
» может быть явно преобразовано в тип «указатель»до T2
”, используя reinterpret_cast
.Результат ссылается на тот же объект, что и источник glvalue, но с указанным типом.[Примечание: то есть для l-значений эталонное приведение reinterpret_cast<T&>(x)
имеет тот же эффект, что и преобразование *reinterpret_cast<T*>(&x)
со встроенными операторами &
и *
(и аналогично для reinterpret_cast<T&&>(x)
).- примечание конца] Временное создание не производится, копирование не производится, конструкторы или функции преобразования не вызываются.
со сноской:
75) Это иногдаобозначаемый как тип pun .
/ 11, неявно, например, содержит ограничения от / 6 до / 10, но, возможно, наиболее распространенное использование (punning объекты ) адресуются в [expr.reinterpret.cast] / 7 :
Указатель объекта может быть явно преобразован в указатель объекта другого типа.Когда значение v
типа указателя объекта преобразуется в тип указателя объекта «указатель на cv T
», в результате получается static_cast<cv T*>(static_cast<cv void*>(v))
.[Примечание: преобразование значения типа «указатель на T1
» в тип «указатель на T2
» (где T1 and T2
- это типы объектов и где требования к выравниванию T2
не являются более строгими, чем требования * 1051)*) и возврат к исходному типу возвращает исходное значение указателя.- примечание конца]
Очевидно, что целью не может быть преобразование в / из указателей или ссылки на void
, так как:
- пример в / 7 ясно демонстрирует, что
static_cast
должно быть достаточно для указателей, как и [expr.static.cast] / 13 и [conv.ptr] / 2 ;и - [преобразования в] ссылки на
void
являются недействительными prima facie .
Далее, [basic.lval] / 8 states:
Если программа пытается получить доступ к сохраненному значению объекта через glvalue другого, чем один из следующих типов, поведение не определено:
(8.1)динамический тип объекта,
(8.2) cv-квалифицированная версия динамического типа объекта,
(8.3) тип, аналогичный динамическому типу объекта,
(8.4) тип, который является типом со знаком или без знака, соответствующим динамическому типу объекта,
(8.5) тип, который является типом со знаком или без знака, соответствующим версии с квалификацией cvдинамического типа объекта,
(8.6) агрегатного типа или типа объединения, который включает один из вышеупомянутых типов среди своих элементов или элементов не статических данных (включая рекурсивно, элемент или нестатические данные)член субагрегата или автономного объединения),
(8.7) тип, который является (возможно, cv-квалифицированным) типом базового класса динамического типа объекта,
(8.8) char, unsigned char или std ::Тип байта.
И если мы вернемся к [expr.reinterpret.cast] / 11 на мгновение мы видим: «Результат относится к того же объекта , что и у источника glvalue, , но с указанным типом .»Это говорит мне как явное утверждение, что результатом reinterpret_cast<T&>(v)
является lvalue ссылка на объект типа T
, доступ к которому явно "через glvalue" "динамического типапредмет".В этом предложении также рассматривается аргумент, что различные параграфы [basic.life] применяются посредством ложного утверждения, что результаты таких преобразований относятся к новому объекту типа T
, время жизни которого еще не былоНачало, которое просто находится на том же адресе памяти, что и v
.
Кажется бессмысленным явное определение таких преобразований только для запрета стандартно определенного использования из результатов , особенно в свете сноски 75, отмечающей, что такое [эталонное] преобразование «иногда упоминается как type pun .»
Обратите внимание, что мои ссылкидо окончательного общедоступного проекта для C ++ 17 (N4659), но рассматриваемый язык мало изменился с N3337 (C ++ 11) до N4788 (C ++ 20WD) (ссылка на отзыв, вероятно, будет относиться к более поздним черновикам во времени).Фактически сноска к [expr.reinterpret.cast] / 11 сделана еще более явной в последнем проекте:
Это иногда упоминается как введите pun , когда результат ссылается на тот же объект, что и источник glvalue.