tmlen
tmlen

Reputation: 9090

C++ type aliasing, where value is replaced

Is the following code legal in 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]);

The second step reads the buffer value as an int, and then writes a float in its place. It never accesses the memory through i_buffer again afterwards, so there is no type aliasing when reading.

However the assignment f_buffer[idx] = writes a float object into an int object, which is UB.

Is there a way to make the compiler consider this to mean that the lifetime of the int should end, and a float object should be constructed in its place, so that there is no type aliassing?

Upvotes: 0

Views: 100

Answers (1)

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136286

However the assignment f_buffer[idx] = writes a float object into an int object, which is UB.

Yep, the above breaks type aliasing rules.

To fix that, for your values you can use a union:

union U {
    float f;
    int i;
};

And then access the corresponding members of the union.

This way when you do:

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

it avoids UB because lifetime of buffer[idx].i ends and that of buffer[idx].f starts.

Upvotes: 2

Related Questions