【问题标题】:Is there a standard-compliant way to determine the alignment of a non-static member?是否有符合标准的方法来确定非静态成员的对齐方式?
【发布时间】:2019-07-03 00:41:30
【问题描述】:

假设我有一些结构 S 和一个非静态成员 member,如下例所示:

struct S { alignas(alignof(void *)) char member[sizeof(void *)]; };

你如何得到member的对齐方式?

运算符 alignof 只能应用于完整类型,而不是表达式 [in 7.6.2.5.1],虽然 GCC 允许,所以 alignof(S::member) 和 Clang 支持它。

没有此限制的“语言律师”标准方法是什么?

另外,sizeof 允许表达式参数,是否存在不对称的原因?

实际关心的是能够获取模板结构成员的对齐方式,您可以通过decltype 获取它们的类型,sizeof 获取它们的大小,但是您还需要对齐方式。

【问题讨论】:

  • 您在寻找什么?您是否希望 alignof(S::member) 成为 alignof(void *)alignof(S) 或其他?我不清楚“非静态成员的对齐”是什么意思。
  • 也许offsetof(S, member); 是你想要的?如果你愿意,可以检查它是否是 4alignof(void *) 的倍数。
  • 我不认为你可以......只需引入一个 constexpr 值,它就是你想要的对齐方式,并在你需要的任何地方使用它而不是 alignof(包括在 alignas 中)(也是alignof in alignas(alignof(void*)) 是多余的)
  • 感谢 cmets,但请注意问题是指语言标准而非实际解决方法
  • @TheCppZoo 谢谢。我了解“非静态成员”的含义。我的观点是“数据成员的对齐”尚不清楚。类型或变量的“对齐”是指该变量可以放置在哪些内存地址:地址必须是对齐的倍数。有了这个想法,数据成员的对齐将描述成员可能位于的地址,即alignof(S),但不是一个干净的倍数,而不是K * alignof(S) + offsetof(S, member) 用于某些K。或者,如果它本身是一个变量,您可能只是要求成员对齐。

标签: c++ language-lawyer typetraits


【解决方案1】:

类型或变量的对齐是对变量可以占用的内存地址的描述——地址必须是对齐的倍数*。但是,对于数据成员,数据成员的地址可以是任何K * alignof(S) + offsetof(S, member)。让我们将数据成员的对齐方式定义为最大可能整数E,这样&some_s.member 始终是E 的倍数。

给定类型S 和成员member,让A = alignof(S), O = offsetof(S, member)
S{}.member 的有效地址是 V = K * A + O 某个整数 K.
V = K * A + O = gcd(A, O) * (K * A / gcd(A, O) + O / gcd(A, O)).
对于K = 1的情况,不存在其他因素。
因此,gcd(A, O) 是对未知 K 有效的最佳因子。

换句话说,"alignof(S.member)" == gcd(alignof(S), offsetof(S, member))

请注意,这种对齐方式始终是 2 的幂,因为 alignof(S) 始终是 2 的幂。

*:在我对标准的短暂探索中,我找不到这个保证,这意味着变量可以的地址是K * alignment + some_integer。但是,这并不影响最终结果。


我们可以定义一个宏来计算数据成员的对齐方式:

#include <cstddef> // for offsetof(...)
#include <numeric> // for std::gcd

// Must be a macro, as `offsetof` is a macro because the member name must be known
// at preprocessing time.
#define ALIGNOF_MEMBER(cls, member) (::std::gcd(alignof(cls), offsetof(cls, member)))

这仅保证对标准布局类型有效,如offsetof is only guaranteed valid for standard layout types。如果类不是标准布局,则有条件支持此操作。

例子:

#include <cstddef>
#include <numeric>

struct S1 { char foo; alignas(alignof(void *)) char member[sizeof(void *)]; };
struct S2 { char foo; char member[sizeof(void *)]; };

#define ALIGNOF_MEMBER(cls, member) (::std::gcd(alignof(cls), offsetof(cls, member)))

int f1() { return ALIGNOF_MEMBER(S1, member); } // returns alignof(void *) == 8
int f2() { return ALIGNOF_MEMBER(S1, foo); }    // returns 8*
int f3() { return ALIGNOF_MEMBER(S2, member); } // returns 1

// *: alignof(S1) == 8, so the `foo` member must always be at an alignment of 8

Compiler Explorer

【讨论】:

  • 我不同意你给出的处理方法,首先,成员的对齐问题没有歧义,因为包含聚合的对齐必须是可知的,此外,可以指定使用alignasmember 进行精确对齐,我认为标准中可能存在的一个错误是没有办法“检索”对齐信息,即使源代码指定了它的最小值.
  • @TheCppZoo 我明白你现在的意思了。您的意思是,“与为成员指定的任何alignas(...) 中的对齐方式相同,或者如果没有alignas(...),则仅使用alignof(member-type)。”您要么必须使用您意识到的 GNU 扩展,要么可以指定对齐方式,例如static constexpr auto member_align = alignof(void *); alignas(member_align) char member[sizeof(void *)]; 以便您可以提取对齐方式。
猜你喜欢
  • 2013-08-25
  • 1970-01-01
  • 1970-01-01
  • 2015-05-08
  • 2017-12-19
  • 2011-03-16
  • 2010-09-16
  • 2013-03-31
相关资源
最近更新 更多