是否有符合标准的方法来确定非静态杆件的对齐方式?
Is there a standard-compliant way to determine the alignment of a non-static member?
>假设我有一些结构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)
。
对于某些整数K
,S{}.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,则结构S1
和S2
可能具有相同的布局,总大小为 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++提供了足够的其他反射功能,以便接近这样的事情,就会有人提出一个现实的建议,添加一种查询非静态数据成员对齐的方法。
- 如何在c++中为模板函数实例创建快捷方式
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 如何以静态代码分析友好的方式使用 #define 防护?
- 是否有符合标准的方法来确定非静态杆件的对齐方式?
- 模板类静态在最终二进制文件中跨共享库以不同方式实例化是否一致
- 如何以静态方式使用另一个 constexpr 数组初始化一个数组
- lib文件是以独占静态方式链接的,还是需要专门编译(VS2015)
- 如何在静态单例类中以编程方式从exec方法返回
- 静态常量字符指针及其使用方式的原因
- 在静态函数中释放内存的最佳方式
- Visual Studio静态链接的应用程序方式太小
- 在 openGL 中带有对象的静态背景。最好的"bake"方式?
- 如何在C++中创建静态全局类?(或者我应该以不同的方式这样做?
- 如何以类似于C函数的方式将dlsym映射到非静态C++成员函数
- 是否可以通过编程方式创建函数静态对象
- c++中静态数据初始化的时间和方式
- 通过元编程或其他方式自动注册dll模块中的静态对象工厂
- 如何以线程安全的方式避免静态数据成员初始化失败
- OMP 目标中全局数组(malloc 与静态)的不同处理方式
- 以静态方式使用Random类