问题描述
一些类型被标准定义为隐式生命周期类型,数组就在其中。
一些函数隐式地创建具有隐式生命周期的对象(malloc 等就在其中),
带有隐式创建具有隐式生命周期的对象的操作列表,可在此处找到。 https://en.cppreference.com/w/cpp/language/object(我希望它是正确的,但对于问题的其余部分,让我们假设 new
在这种情况下用作 malloc
用于隐式对象创建目的)。
如果不创建其元素,隐式创建数组是什么意思?这是否意味着
T* implicit_array = reinterpret_cast<T*>(::operator new(sizeof(T) * count,std::align_val_t{alignof(T)}) );
产生适用于指针运算的implicit_array对象,即为T类型的元素提供有效的存储空间,以便稍后使用placement new构造?
这是否意味着 new (implicit_array + i) T{...}
是一个定义明确的操作,即使按照标准,implicit_array + i
不一定定义? https://eel.is/c++draft/expr.unary#op-3.2。
或者它的意思
std::byte* memory =
reinterpret_cast<std::byte*>(::operator new(sizeof(T) * capacity,std::align_val_t{alignof(T)}));
new (memory) T{args1 ...}
// build more objects
new (memory + (k-1)*sizeof(T) ) T{args_k ...}
T* implicit_array = std::launder(reinterpret_cast<T*>(memory) ); // does it produce array of k elements?
将 implicit_array
视为具有 k 个元素的数组?
谢谢。
解决方法
可以先为数组分配存储空间,然后再构造元素。 expr.new/15 中的一个密切相关的注释特别提到“分配字符数组的常见习惯用法”。
发布的代码大多遵循正确的分配-然后构造序列,除了 reinterpret_cast
不安全,实际上没有必要。从分配步骤中返回的 void *
到类型化 T *
的转换是在构造时由放置 new
运算符完成的。
void *alloc_array(size_t cnt)
{
return ::operator new(sizeof(T) * cnt,std::align_val_t{alignof(T)});
}
T *construct_array(void *buf,size_t cnt)
{
T *arr = new(buf) T {...},*p = arr;
for(int i = 1; i < cnt; i++)
p = new(p + 1) T {...};
return arr;
}
void destruct_array(T *arr,size_t cnt)
{
for(int i = cnt; i--; )
arr[i].~T();
}
void free_array(void *buf,size_t cnt)
{
::operator delete(buf,sizeof(T) * cnt,std::align_val_t{alignof(T)});
}
示例用法:
void *buf = alloc_array(cnt);
T *arr = construct_array(buf,cnt);
for(int i = 0; i < cnt; i++)
T &ob = arr[i]; /* ... */
destruct_array(arr,cnt);
free_array(buf,cnt);