如何在C ++中创建具有动态对齐要求的对象? [英] How to create objects in C++ with dynamic alignment requirements?

查看:97
本文介绍了如何在C ++中创建具有动态对齐要求的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++中分配和使用具有动态指定对齐方式的缓冲区的正确方法是什么?我想到的用例是Vulkan动态统一缓冲区(请参阅之前的问题,其中抽象地讨论了所需的过程) ),其对齐方式的约束是通过VkPhysicalDeviceLimitsminUniformBufferOffsetAlignment属性给出的,在编译时尚不知道.

What is the correct way in C++ to allocate and use a buffer with dynamically-specified alignment? The use case that I had in mind was Vulkan dynamic uniform buffers (see this previous question which discusses the required process in the abstract), for which a constraint on alignment is given via the minUniformBufferOffsetAlignment property on VkPhysicalDeviceLimits, which is not known at compile time.

我最初以为我可以使用operator new(std::align_val_t)做类似的事情

I initially thought I might be able to use operator new(std::align_val_t) doing something like

Foo* buffer = new(std::align_val_t{alignment}) Foo[n];

但是不能编译(至少在MSVC上).

but that doesn't compile (at least on MSVC).

我还观看了Timur Doumler的CppCon演示文稿"在现代C ++中进行类型调整 a>"表示在std::aligned_alloc之类的结果上使用reinterpret_cast会导致不确定的行为.

I've also watched Timur Doumler's CppCon presentation "Type punning in modern C++" which notes that using reinterpret_cast on the result of something like std::aligned_alloc leads to undefined behaviour.

到目前为止,我已经提出了以下建议:

So far I've come up with the following:

std::size_t n = getNumberOfElements(); // possibly not known at compile time
std::size_t alignment = getRequiredAlignment(); // not known at compile time

makeSureMultiplicationDoesNotOverflow(sizeof(Foo), n); // details irrelevant

void* storage = std::aligned_alloc(sizeof(Foo) * n, alignment); // _aligned_malloc on MSVC
if (!storage) { std::terminate(); }
Foo* buffer = new(storage) Foo[n];

// do stuff with buffer

for(std::size_t i = 0; i < n; ++i) { buffer[i].~Foo(); }
std::free(storage); // _aligned_free on MSVC

我在这里错过了会导致不确定行为的东西吗?

Have I missed something here that's going to cause undefined behaviour?

我注意到上面没有将对齐方式应用到第一个对象以外的任何对象,因此这肯定是个糟糕...

I noticed that the above does not apply the alignment to any objects other than the first one, so that's definitely an oops...

(显然,在实际的应用程序中,应将其封装到一个类中以提供RAII,但现在将其省略以免使示例代码膨胀.)

(Obviously in a real application this should be encapsulated into a class to provide RAII, but leaving that out for now so as not to bloat the example code.)

推荐答案

您可以执行以下操作:

auto storage = static_cast<Foo*>(std::aligned_alloc(n * sizeof(Foo), alignment));
std::uninitialized_default_construct_n(storage, n);
auto ptr = std::launder(storage);

// use ptr to refer to Foo objects

std::destroy_n(storage, n);
free(storage);

std::aligned_alloc返回的指针投射到Foo*并不是未定义的行为.如果您尝试在std::aligned_alloc之后在std::uninitialized_default_construct_n创建Foo对象之前立即取消引用它,它将是UB.

Casting a pointer returned by std::aligned_alloc to Foo* is not undefined behavior. It would be UB if you tried to dereference it right after std::aligned_alloc before std::uninitialized_default_construct_n created Foo objects.

编辑.

上面的代码是技术上未定义的行为.但是似乎在C ++中,没有UB的情况下没有100%符合标准的方式来进行这种分配.从实际的角度来看,此代码是可靠且安全的. std::launder(storage)可能应该用于通过storage指针访问Foo对象.

The code above is technically undefined behavior. But it seems that in C++ there is no 100% standard-conforming way to do such an allocation without UB. From the practical point to view this code is reliable and safe. std::launder(storage) should probably be used to access Foo objects through storage pointer.

有关详情,请参见此问题和讨论.

See this question for details and discussions.

这篇关于如何在C ++中创建具有动态对齐要求的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆