将奇异对齐的对象置于协程状态是否是定义的行为?

Fre*_*ler 12 c++ memory-alignment language-lawyer c++20 c++-coroutine

编辑:感谢大家的回答和回复。Language Lawyer 的答案在技术上是正确的,因此被接受,但 Human-Compiler 的答案是唯一符合赏金标准(获得 2+ 分)的答案,或者对问题的特定主题进行了充分阐述。


完整问题

将对象b 置于协程状态是否定义了行为(例如,将其作为参数,或将其保留在暂停点上),在哪里alignof(b) > __STDCPP_DEFAULT_NEW_ALIGNMENT__

例子:

inline constexpr size_t large_alignment =
    __STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2;

struct alignas(large_alignment) behemoth {
  void attack();
  unsigned char data[large_alignment];
};

task<void> invade(task_queue &q) {
  behemoth b{};
  co_await submit_to(q);
  b.attack();
}
Run Code Online (Sandbox Code Playgroud)

解释

当调用协程时,协程状态的堆内存通过operator new.

此调用operator new 可能采用以下形式之一:

  1. 按照请求的大小传递传递给协程的所有参数,或者如果找不到这样的重载,
  2. 仅传递请求的大小。

无论调用采用哪种形式,请注意它不使用接受 a 的重载std::align_val_t,这是分配必须对齐多于 的内存所必需的__STDCPP_DEFAULT_NEW_ALIGNMENT__。因此,如果__STDCPP_DEFAULT_NEW_ALIGNMENT__ 必须将对齐大于 的对象保存在协程状态中,则应该无法保证该对象最终会在内存中正确对齐。


实验

神箭

async f(): Assertion `reinterpret_cast<uintptr_t>(&b) % 32ull == 0' failed.
Run Code Online (Sandbox Code Playgroud)

所以它绝对不能在 GCC 主干 ( 11.0.1 20210307) 上工作。替换3216(等于__STDCPP_DEFAULT_NEW_ALIGNMENT__)消除了这个断言失败。

Godbolt.org 无法运行 Windows 二进制文件,但断言也在我的计算机上使用 MSVC 触发。

Lan*_*yer 0

来自[basic.align]/3

是否支持任何扩展对齐以及支持它们的上下文是由实现定义的。

所以答案是:实现定义的。这句话的第二部分听起来像是一个实现可能支持在普通函数中创建过度对齐类型的对象,但在协程中不支持。

[基本.对齐]/9 :

如果实现不支持对特定上下文中的特定扩展对齐的请求,则该程序是格式错误的。

当不支持扩展对齐时不发出诊断消息的实现是不合格的。

  • 这并不“那么”荒谬。只需分配额外的“max(alignof(captured-things)) + sizeof(void*)”字节,并将整个帧定位在分配内的合适偏移处。额外的“void*”是为了记住分配的开始,以便可以释放它。 (2认同)