Это является продолжением этого другого вопроса о повторном использовании памяти. Поскольку первоначальный вопрос касался конкретной реализации, ответ был связан с этой конкретной реализацией.
Поэтому я задаюсь вопросом, законно ли в согласованной реализации повторно использовать память массива фундаментального типа для массива другого типа:
- оба типа являются фундаментальным типом и как таковые имеют тривиальный dtor и по умолчанию ctor
- оба типа имеют одинаковый размер и требования к выравниванию
Я закончил следующим примером кода:
#include <iostream>
constexpr int Size = 10;
void *allocate_buffer() {
void * buffer = operator new(Size * sizeof(int), std::align_val_t{alignof(int)});
int *in = reinterpret_cast<int *>(buffer); // Defined behaviour because alignment is ok
for (int i=0; i<Size; i++) in[i] = i; // Defined behaviour because int is a fundamental type:
// lifetime starts when is receives a value
return buffer;
}
int main() {
void *buffer = allocate_buffer(); // Ok, defined behaviour
int *in = static_cast<int *>(buffer); // Defined behaviour since the underlying type is int *
for(int i=0; i<Size; i++) {
std::cout << in[i] << " ";
}
std::cout << std::endl;
static_assert(sizeof(int) == sizeof(float), "Non matching type sizes");
static_assert(alignof(int) == alignof(float), "Non matching alignments");
float *out = static_cast<float *>(buffer); // (question here) Declares a dynamic float array starting at buffer
// std::cout << out[0]; // UB! object at &out[0] is an int and not a float
for(int i=0; i<Size; i++) {
out[i] = static_cast<float>(in[i]) / 2; // Defined behaviour, after execution buffer will contain floats
// because float is a fundamental type and memory is re-used.
}
// std::cout << in[0]; // UB! lifetime has ended because memory has been reused
for(int i=0; i<Size; i++) {
std::cout << out[i] << " "; // Defined behaviour since the actual object type is float *
}
std::cout << std::endl;
return 0;
}
Я добавил комментарии, объясняющие, почему я считаю, что этот код должен определять поведение. И ИМХО все в порядке и стандартное соответствие AFAIK, но я не смог найти, является ли строка, помеченная вопросом здесь, или недействительна.
Объекты Float повторно используют память из объектов int, поэтому время жизни ints заканчивается, когда начинается время жизни поплавков, поэтому правило сглаживания не должно быть проблемой. Массив был динамически распределен, поэтому объекты (int и floats) фактически созданы в массиве типа void, возвращаемом operator new
. Поэтому я думаю, что все должно быть в порядке.
Но поскольку он позволяет заменять объект низкого уровня, который обычно не одобряется в современных C++, я должен признать, что у меня есть сомнения...
Итак, вопрос: делает ли над кодом код UB, и если да, то где и почему?
Отказ от ответственности: я бы посоветовал против этого кода в переносной кодовой базе, и это действительно вопрос юриста на языке.