Согласно последнему заседанию комитета ISO C++, битовый будет введен в стандарт C++ 20.
Я знаю, что reinterpret_cast не подходит для этой работы из-за правила псевдонима типов, но мой вопрос в том, почему они решили не расширять reinterpret_cast, чтобы рассматривать объект как представление битовой последовательности, и предпочли предоставить эту функциональность как новую языковую конструкцию?
@NicolBolas: Как насчет того, чтобы позволить reinterpret_cast делать это: float x = 1.0f; reinterpret_cast<unsigned int>(x);.
Я думал в точности так, как предложил @geza
@geza: reinterpret_cast уже имеет определенное значение для этого, так что это изменит семантику reinterpret_cast, а не расширяет ее.
@ChrisDodd: Я не думаю, что это правда. Какое значение это имеет в настоящее время?
@geza reinterpret_cast<unsigned>(1.0f) == 1U; - выполняет преобразование типа float-> int.
@ChrisDodd: нет, это преобразование в настоящее время плохо сформировано.
На самом деле это было давным-давно предложенный.





Что ж, есть одна очевидная причина: потому что он не будет делать все, что делает bit_cast. Даже в мире C++ 20, где мы можем выделять память во время компиляции, reinterpret_cast запрещен в функциях constexpr. Одна из явных целей bit_cast - иметь возможность делать такие вещи во время компиляции:
Furthermore, it is currently impossible to implement a
constexprbit-cast function, asmemcpyitself isn’tconstexpr. Marking the proposed function asconstexprdoesn’t require or preventmemcpyfrom becomingconstexpr, but requires compiler support. This leaves implementations free to use their own internal solution (e.g. LLVM has abitcastopcode).
Теперь вы можете сказать, что можете просто расширить это конкретное использование reinterpret_cast на контексты constexpr. Но это усложняет правила. Вместо того, чтобы просто знать, что reinterpret_cast нельзя использовать в период кода constexpr, вы должны помнить конкретные формы reinterpret_cast, которые нельзя использовать.
Также есть практические проблемы. Даже если вы хотите пойти по пути reinterpret_cast, std::bit_cast - это библиотечная функция. И всегда легче получить функцию библиотеки через комитет, чем функцию языка, даже если она получит некоторую поддержку компилятора.
Тогда есть более субъективные вещи. reinterpret_cast обычно считается опасной по своей сути операцией, что свидетельствует о некотором "обмане" системы типов. Напротив, bit_cast - нет. Он создает новый объект, как будто копируя его представление значения из существующего. Это низкоуровневый инструмент, но он не мешает работе системы типов. Поэтому было бы странно писать «безопасную» операцию так же, как «опасную».
В самом деле, если вы написали их таким же образом, это начнет вызывать вопросы, почему это достаточно четко определено:
float f = 20.4f;
int i = reinterpret_cast<int>(f);
Но это как-то плохо:
float f = 20.4f;
int &i = reinterpret_cast<int &>(f);
И, конечно же, юрист по языку или кто-то, кто знаком со строгим правилом псевдонима, поймет, почему последнее плохо. Но для непрофессионала, если использование reinterpret_cast для преобразования битов нормально, неясно, почему неправильно использовать reinterpret_cast для преобразования указателей / ссылок и интерпретации существующего объекта как преобразованного типа.
Разные инструменты следует писать по-разному.
Вторая из ваших последних форм должна быть определена в тех случаях, когда все использование i происходит до следующего обращения к хранилищу или обращения к нему конфликтным образом (записи конфликтуют с чтениями или записями; чтения не конфликтуют с чтениями) с помощью средств, не полученных из i, или выполнение достигает начала функции или тела истинного цикла, в котором это произойдет. Правило «Строгое наложения» было предназначено для того, чтобы не требовать от компиляторов пессимистичного отношения к вещам, которые они не видят, а не для того, чтобы побуждать их игнорировать то, что они могут.
Что касается «reinterpret_cast обычно считается опасной по своей сути операцией» ... Я думаю, это не объясняет, вероятно, наиболее распространенный вариант использования reinterpret_cast, который я вижу, что-то вроде reinterpret_cast<unsigned char *>(ptr). Это не более опасная или «обманчивая» система типов, чем static_cast<unsigned char *>(static_cast<void *>(ptr)).
@ user541686: Это также запрещено в коде constexpr. Вы можете constexprbit_cast в байтовый массив. И нет, это не опаснее; это просто дольше.
Существует фундаментальное несоответствие между современной строгой интерпретацией языковых стандартов C и C++ компиляторами в высокоуровневой языковой природе и представлением о том, что вы можете использовать reinterpret_cast для переинтерпретации группы байтов как других объектов. Обратите внимание, что так называемое правило «строгого псевдонима» в большинстве случаев не может даже использоваться для дисквалификации любой попытки переинтерпретации байтов, поскольку код изначально не определял бы поведение: reinterpret_cast<float*>(&Int) даже не является указателем на объект с плавающей запятой, это указатель на целое число неправильного типа. Вы не можете разыменовать его, поскольку в этом месте не создан объект типа float; если бы он был, его жизнь не началась бы; и если бы его время жизни началось, оно было бы неинициализированным.
Байты, которые представляют собой действительный образец битов с плавающей запятой, просто не могут быть интерпретированы как таковые, если у вас нет здесь подходящего объекта с плавающей запятой.
Допустимый ненулевой указатель - это не просто типизированное значение начального адреса области, которая оказывается правильно выровненной; ненулевой действительный указатель указывает на конкретный объект (или один за концом массива или тривиальный «массив» одного объекта).
Я даже не вижу санкционированных "строгих псевдонимов" приведений скалярной переинтерпретации (смесь со знаком / без знака) как возможно допустимые, поскольку по этому адресу существует целочисленный объект без знака (соответственно, без знака) (и компилятор, очевидно, не может использовать беззнаковый (соответственно . подписано) исходное значение тоже).
В любом случае, C++ имеет неправильный дизайн, потому что он представляет собой смесь разных языков (некоторые очень низкого уровня, некоторые очень высокого уровня) и сильно сломан.
"так называемое правило "строгого псевдонима" в большинстве случаев не может использоваться даже для дисквалификации любой попытки переинтерпретации байтов, поскольку код изначально не определял бы поведение" ... что? Это правило строгого псевдонима, что причины этот код должен быть UB. Без этого правила стандарт был бы неполным, потому что не было бы утверждения о его поведении. Есть разница между «это UB» и «стандарт в этой области не определен».
@NicolBolas Кто сказал, что стандартная инструкция должна быть полной? Как он сейчас завершен? Что в любом случае означает разыменование указателя reinterpret_casted? Если транслировать на char* можно, то почему не на short* или float*? Если можно использовать традиционно memcpy, а недавно - bit_cast, почему нельзя разыменовать приведенный указатель?
@NicolBolas В C, где определяется поведение memcpy одним FILE по сравнению с другим? Все ли вызовы memcpy полностью определены или явно сделаны UB?
Каким образом вы предлагаете расширить
reinterpret_cast? То есть, как бы гипотетически выглядел код, использующий это?