【问题标题】:Why does the C++ standard allow std::max_align_t and __STDCPP_DEFAULT_NEW_ALIGNMENT__ to be inconsistent?为什么 C++ 标准允许 std::max_align_t 和 __STDCPP_DEFAULT_NEW_ALIGNMENT__ 不一致?
【发布时间】:2019-10-03 21:42:46
【问题描述】:

在 Visual Studio 中,编译 64 位时:

  • sizeof(std::max_align_t) 是 8
  • __STDCPP_DEFAULT_NEW_ALIGNMENT__ 是 16

所以尽管std::max_align_t 表示new 的实现应该返回对齐到8 字节的倍数的指针,但要求对齐为16 字节的分配不调用void* operator new (std::size_t count, std::align_val_t); 方法,而是调用void* operator new (std::size_t count);(参见https://en.cppreference.com/w/cpp/memory/new/operator_new) 并期望它们返回一个在 16 字节上对齐的指针。

所以分配一个这样定义的结构:

struct alignas(16) S {double m_value;};

将调用标准运算符new(不带std::align_val_t 参数)并期望它在16 个字节上对齐,而std::max_align_t 只指定它应该在8 个字节上对齐。

这意味着当否决 new 运算符时,您必须将所有内容至少对齐 16 个字节,即使 8 个字节就足够了。

  • 我错过了什么吗?
  • 这是 Visual Studio 实现 C++/STL 的方式中的错误吗?
  • 或者这是 C++/STL 标准中的错误?

【问题讨论】:

  • [basic.align]/3 提到了这两个常量,但我不确定它们的值之间的关系意味着什么。由于该标准为大于每个对齐的对齐提供了不同的名称,因此我认为(故意)不能保证它们是相同的。但不能告诉你为什么。
  • 在这种情况下它不会改变结果,但我认为你的意图是alignof(std::max_align_t),而不是sizeof(std::max_align_t)

标签: c++ visual-studio c++-standard-library


【解决方案1】:

C++17 中有两层过度对齐的类型:extended 和 new-extended。 std::max_align_t 定义未扩展的最大对齐,__STDCPP_DEFAULT_NEW_ALIGNMENT__ 定义未新扩展的最大对齐。

新扩展对齐,顾名思义,就是你分配的东西与new的对齐。

基本上,常规的operator new 将返回适合任何对象的内存,直到新扩展的对齐大小。任何更大的对齐方式都更喜欢使用operator new 重载来指定所创建类型的对齐方式。当然,有条件地支持,就像一般的过度对齐类型一样。 operator delete 调用也是如此,以破坏与此类类型关联的内存。

Visual Studio 的意思是不被认为是过度对齐的最大对齐是 8 字节,但 operator new 分配的内存对齐是 16 字节。


这意味着当否决 new 运算符时,您必须在至少 16 个字节上对齐所有内容,即使 8 个字节就足够了。

基本上,是的。无法要求实现告诉您原始 operator new/delete 重载请求的对齐方式。

现在,您可以逐个对象重载该对象的 operator new 以直接调用对齐特定的 operator new。但是你不能让编译器这样做。

【讨论】:

  • 我更正了我的问题中的一个打字错误:对齐要求为 16 个字节的分配不调用 void* 运算符 new (std::size_t count, std::align_val_t); (我忘记了 align_val_t)。 en.cppreference.com/w/cpp/memory/new/operator_new 明确指出,当“对齐要求超过 STDCPP_DEFAULT_NEW_ALIGNMENT 的单个对象需要存储空间”时,将调用带有 std::align_val_t 参数的 new 运算符。因此,这会强制实现返回按 16 个字节对齐的指针,而 std::max_align_t 声明它必须是 8 个字节(默认情况下)。
  • @Patrick:“虽然 std::max_align_t 声明它必须是 8 个字节。”它声明没有这样的事情。这只是类型被认为具有扩展对齐之前的最大大小。它没有说明从 operator new 返回的指针的对齐方式。
  • 你是对的。假设std::max_align_t 也说明了operator new 返回的指针对齐,这对我来说是错误的。尽管如此,如果__STDDPP_DEFAULT_NEW_ALIGNMENT__ 是16,这意味着如果你只需要8 个字节,就会丢失很多内存(当然4 个字节也是如此)。
  • 好的,但是为什么会这样呢?为什么不让 new 也总是返回 8 字节对齐?
  • @JosephGarvin:因为需要 16 字节对齐的类型在 C++ 的默认 new 提供明确请求分配此类对齐的机制之前已经存在了十多年。为了允许用户使用这些类型,微软决定让他们的默认分配器总是返回 16 字节对齐的指针。
猜你喜欢
  • 2015-10-06
  • 1970-01-01
  • 2017-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多