Всего пару недель назад я узнал, что стандарт С++ имеет строгое правило псевдонимов. В принципе, я задал вопрос о смещении битов - вместо того, чтобы менять каждый байт по одному, чтобы максимизировать производительность, я хотел загрузить собственный собственный процессорный регистр (соответственно 32 или 64 бита) и выполнить сдвиг 4/8 байтов в одной команде.
Это код, который я хотел бы избежать:
unsigned char buffer[] = { 0xab, 0xcd, 0xef, 0x46 };
for (int i = 0; i < 3; ++i)
{
buffer[i] <<= 4;
buffer[i] |= (buffer[i + 1] >> 4);
}
buffer[3] <<= 4;
И вместо этого я хотел использовать что-то вроде:
unsigned char buffer[] = { 0xab, 0xcd, 0xef, 0x46 };
unsigned int *p = (unsigned int*)buffer; // unsigned int is 32 bit on my platform
*p <<= 4;
Кто-то вызвал в комментарии, что мое предложенное решение нарушило правила слияния С++ (поскольку p был типа int*
, а буфер был типа char*
, и я разыменовал p для выполнения сдвига. (Пожалуйста, игнорируйте возможные проблемы выравнивания и порядка байтов - я обрабатываю те, что находятся за пределами этого фрагмента). Я был очень удивлен, узнав о правиле строкового слияния, поскольку я регулярно работаю с данными из буферов, отбрасывая их от одного типа к другому и никогда не имел никаких проблем. расследование показало, что используемый мною компилятор (MSVC) не применяет строгие правила псевдонимов, и поскольку я только разрабатываю gcc/g++ в свое свободное время в качестве хобби, я, вероятно, еще не сталкивался с проблемой.
Итак, я задал вопрос о строгих правилах псевдонимов и новом операторе размещения на С++:
IsoCpp.org предлагает часто задаваемые вопросы о размещении новых и предоставляет следующий пример кода:
#include <new> // Must #include this to use "placement new"
#include "Fred.h" // Declaration of class Fred
void someCode()
{
char memory[sizeof(Fred)]; // Line #1
void* place = memory; // Line #2
Fred* f = new(place) Fred(); // Line #3 (see "DANGER" below)
// The pointers f and place will be equal
// ...
}
Пример достаточно прост, но я спрашиваю себя: "Что, если кто-то вызовет метод на f
- например, f->talk()
? В этот момент мы будем разыменовывать f
, что указывает на то же в памяти memory
(типа char*
. Я читал множество мест, где есть исключения для переменных типа char*
для псевдонимов любого типа, но у меня создалось впечатление, что это не" два -worth street "- означает, char*
может псевдоним (чтение/запись) любого типа T
, но тип T
может использоваться только для псевдонима char*
, если T
имеет значение char*
. Когда я набираю это, это не имеет никакого смысла для меня, и поэтому я склоняюсь к убеждению, что утверждение о том, что мой первоначальный (пример смещения битов) нарушил правило строгой псевдонимы, неверен.
Может кто-нибудь объяснить, что правильно? Я сходил с ума, пытаясь понять, что является законным, а что нет (несмотря на то, что читал многочисленные сайты и сообщения SO по теме)
Спасибо