是否有符合标准的方法来确定非静态杆件的对齐方式?

Is there a standard-compliant way to determine the alignment of a non-static member?

本文关键字:静态 方式 对齐 标准 方法 是否      更新时间:2023-10-16

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

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

你如何获得member的对齐?

运算符alignof只能应用于完整类型,而不能应用于表达式 [在 7.6.2.5.1 中],尽管 GCC 允许这样做,因此alignof(S::member)和 Clang 支持它。

没有这种限制的"语言律师"标准方法是什么?

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

实际问题是能够获得模板结构成员的对齐方式,您可以执行decltype来获取它们的类型,sizeof来获取它们的大小,但是您还需要对齐。

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

给定一个带有成员member的类型S,设A = alignof(S), O = offsetof(S, member)
对于某些整数KS{}.member的有效地址V = K * A + O
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仅保证对标准布局类型有效。如果类不是标准布局,则有条件地支持此操作。

例:

#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

编译器资源管理器

我认为这是不可能的。在一般情况下,使用对齐说明符声明非静态数据成员可能不会更改包含它的类的布局。在下面的示例中,如果(最常见的)int的大小和对齐方式为 4,则结构S1S2可能具有相同的布局,总大小为 8 个字节。每个末尾可能有 3 个字节的填充:

struct S1 {
int x;
char y;
};
struct S2 {
int x;
alignas(4) char y;
};

这使我们无法使用有关结构布局的任何信息来确定y的对齐方式。正如OP所指出的,alignof(S::member)是无效的。

顺便说一下,也没有任何方法可以查询常规变量的对齐说明符。您可以使用std::align函数检查变量是否分配给了与对齐方式为 X 的对象适当对齐的地址,但这并不意味着该变量实际上是以 X 或更大的对齐方式声明的。它可能以小于 X 的对齐方式声明,巧合地最终被分配到一个可能支持对齐方式为 X 的对象的地址。

由于此功能不仅不支持非静态数据成员,还不支持常规变量,因此我倾向于认为这不是疏忽;故意不支持此功能,因为它没有用。编译器需要知道对齐说明符,以便它可以适当地分配变量或数据成员。这不是程序员的工作。当然,程序员可能需要知道类型的对齐要求,以便为该类型的实例适当地分配内存,但是作为程序员,您不能创建变量的其他实例,除非通过触发一些使其自动发生的条件(例如,继续循环的下一次迭代将在循环主体中释放和重新分配自动变量)。到目前为止,您也不能在编译时创建保证与给定类布局兼容的第二个类,这是我能想到的假设"非静态数据成员的查询对齐"功能的主要应用程序。我希望,一旦C++提供了足够的其他反射功能,以便接近这样的事情,就会有人提出一个现实的建议,添加一种查询非静态数据成员对齐的方法。