这是另一个的后续question https://stackoverflow.com/q/51930334/3545273关于内存重用。由于最初的问题是关于具体实现的,因此答案与该具体实现相关。
所以我想知道,在一致的实现中,将基本类型数组的内存重新用于提供的不同类型的数组是否合法:
- 两种类型都是基本类型,因此具有简单的 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 标准符合,但我无法找到该行是否标记在这里提问是否有效。
浮点数对象会重复使用 int 对象的内存,因此当浮点数的生命周期开始时,整数的生命周期也结束,因此严格别名规则应该不是问题。数组是动态分配的,因此对象(int 和 float)实际上都是在 a 中创建的空型返回的数组operator new
。所以我认为一切都应该没问题。
但由于它允许低级对象替换,这在现代 C++ 中通常是不受欢迎的,所以我必须承认我有一个疑问......
所以问题是:上面的代码是否调用了 UB,如果是,在哪里以及为什么?
免责声明:我建议不要在可移植代码库中使用此代码,这实际上是一个语言律师问题。