Псевдоним типа C ++, где значение заменяется - PullRequest
0 голосов
/ 04 октября 2018

Допустим ли следующий код в C ++?

int get_i(int idx) { ... }
float transform(int i) { ... }
void use(float f) { ... }

static_assert(sizeof(int) == sizeof(float));
void* buffer = std::malloc(n * sizeof(int));

int* i_buffer = reinterpret_cast<int*>(buffer);
float* f_buffer = reinterpret_cast<float*>(buffer);

// Fill int values into the buffer
for(int idx = 0; idx < n; ++idx)
    i_buffer[idx] = get_i(idx);

// Transform int value to float value, and overwrite
// (maybe violates strict aliassing rule?)
for(int idx = 0; idx < n; ++idx)
    f_buffer[idx] = transform(i_buffer[idx]);

for(int idx = 0; idx < n; ++idx)
    use(f_buffer[idx]);

На втором шаге значение буфера считывается как int, а затем записывается float вместо него.Впоследствии он никогда не обращается к памяти через i_buffer снова, поэтому при чтении нет псевдонимов типов.

Однако назначение f_buffer[idx] = записывает объект float в объект int, который является UB.

Есть ли способ заставить компилятор считать, что это означает, что время жизни int должно закончиться, и на его месте должен быть создан объект float, чтобы не было псевдонимов типов?

1 Ответ

0 голосов
/ 04 октября 2018

Тем не менее, присваивание f_buffer[idx] = записывает объект с плавающей точкой в ​​объект int, который является UB.

Да, вышеприведенные нарушения нарушают правила наложения типов .

Чтобы исправить это, для ваших значений вы можете использовать объединение:

union U {
    float f;
    int i;
};

И затем получить доступ к соответствующим членам объединения.

Таким образом, когда вы делаете:

buffer[idx].i = ...; // make i the active union member
...
buffer[idx].f = transform(buffer[idx].i); // make f the active union member

он избегает UB, поскольку время жизни buffer[idx].i заканчивается, а время жизни buffer[idx].f начинается.

...