在C++11中,我可以在哪里使用alignas()?

37
为了标准化我的代码并使其更具可移植性,我进行了替换。
#ifdef __GNUC__
typedef __attribute__((aligned(16))) float aligned_block[4];
#else
typedef __declspec(align(16)) float aligned_block[4];
#endif

随着

typedef float alignas(16) aligned_block[4];

C++11中出现了该问题。但是gnu(4.8)不喜欢它,而是抱怨。

test.cc:3:9: warning: attribute ignored [-Wattributes]
  typedef float alignas(16) aligned_block[4];
                ^
test.cc:3:9: note: an attribute that appertains to a type-specifier is ignored

然而,clang 3.2不会创建任何警告(即使使用-Weverything -Wno-c++98-compat -pedantic)。因此,我想知道我的代码是否正确,更一般地说,alignas()可以放置在哪里以及不能放置在哪里。

编辑(Apr 2013)

有关标准的相关文章是7.6.2,特别是7.6.2.1

对变量或类数据成员可以应用对齐指定符,但不得应用于位域、函数参数、catch子句的形式参数(15.3)或使用register存储类说明符声明的变量。对齐指定符还可以应用于类或枚举类型的声明。带省略号的对齐指定符是一个包扩展(14.5.3)。

如Red XIII已经挖掘出来的那样。然而,我不够专业,不知道这对我上面的测试意味着什么。

如果clang接受我的属性这一事实有任何意义,也许值得提到的是,当尝试使用using指令而不是typedef时,clang也会发出警告。并且,与本问题早期版本中的一种陈述相反,gcc不仅会发出警告,而且确实忽略了我对齐的意愿。


你是否在使用 gcc -std=c++11 选项? - iefserge
3
该命令为编译C++代码所用,其中-std=c++11表示使用C++11标准进行编译,-Wextra-Wall-pedantic为编译时的警告选项,可以检查代码中可能存在的问题。 - Walter
4个回答

37

我认为你只是放错了alignas的位置。如果你把它直接放在标识符之后,无论是GCC还是Clang都能够理解并应用对齐:

typedef float aligned_block alignas(16) [4];
typedef float aligned_block [4] alignas(16);

如果你使用using,这也是正确的,而且区别也更加明显。这里有两个版本,GCC不接受(警告,忽略对齐):

using aligned_block = float alignas(16)[4];
using aligned_block = float[4] alignas(16);

这是被接受的答案:

using aligned_block alignas(16) = float[4];
我认为GCC适用于typedef。通过alias-declaration可以引入typedef-name。在using关键字后面的identifier成为typedef-name,而跟随identifier可选的attribute-specifier-seq与该typedef-name相关联。它的语义与如果由typedef指定符引入的相同。对于typedef的规则遍布几个段落,包括在§8.3/1的末尾,其中你可以找到:“在declarator-id之后跟随的可选的attribute-specifier-seq与声明的实体相关。”更新:上述答案集中在alignas必须放置的位置上,而不是其确切含义。考虑以下例子。一个alignment-specifier可以应用于变量或类数据成员,但不得应用于位字段、函数参数、exception-declaration(15.3)或使用register存储类别说明符声明的变量。可以在类的声明或定义(分别在elaborated-type-specifier(7.1.6.3)或class-head(第9条)中)以及枚举的声明或定义(分别为opaque-enum-declarationenum-head,如7.2所述)中应用一个alignment-specifier。带有省略号的alignment-specifier是一种包扩展(14.5.3)。它列出了可以明确应用的情况和不能明确应用的情况。上述问题的示例都不是这种情况。人们还可以认为由typedefusing创建的类型别名作为别名类型的一部分携带了对齐规范。然后可以使用此别名创建变量等,如7.6.2p1所允许,但不能创建带有register等的变量。从这个意义上讲,我认为属性说明符以延迟的方式应用(在7.6.2的意义下),因此当在语法上正确的位置放置对齐规范时,OP的示例仍然有效。

5
无论是typedef还是“已接受”的using语句,使用clang都会报错: 错误:'alignas'属性只适用于变量、数据成员和标记类型。 - jjrv
4
这个答案是错误的。根据[dcl.align]p1,对齐说明符只能应用于变量、数据成员或类或枚举的声明。这不属于这些东西之一,所以它是不合法的。 - Richard Smith
1
@DanielFrey 您说“Clang和GCC都很高兴”,这是不正确的。 Clang拒绝了您所有试图将alignas应用于类型的尝试。关于您的更新:含义是alignas只能放置在7.6.2中列出的位置,而不能放置在其他任何地方(请注意,可以应用它的地方列表中仅删除了其余情况下它无法应用的情况)。剩余情况最好是未定义行为(由于缺乏规范)。 - Richard Smith
1
@RichardSmith 我不记得我是如何测试Clang的,也不知道当时用的是哪个版本,可能当时我没有测试好。无论如何:为什么要有意使上述UB?谁有这个意图,为什么?我有看不到的问题吗?GCC可以在类型别名中“存储”对齐方式,并且它会在使用时应用 - Daniel Frey
4
是的,这是一个问题;GCC的对齐属性不会给你一个新类型,而是给你一个有缺陷的类型,只有在某些情况下才会记得它的对齐方式。例如,当把该类型传递到模板中时,对齐信息就会丢失。 - Richard Smith
显示剩余4条评论

19
您不能对 typedef 应用对齐方式。在 C++ 的对齐规范模型中,对齐是类型本身的不可分割的一部分,而 typedef 不会创建一个新类型(它只提供现有类型的新名称),因此在 typedef 声明中应用对齐规范是没有意义的。
根据 [dcl.align] (7.6.2)p1

对齐规范 可以应用于变量或类数据成员[...]中。 对齐规范 也可以应用于类的声明或定义(在详述类型说明符(7.1.6.3)或类头(Clause 9)中,分别)和枚举的声明或定义(在 不透明的枚举声明枚举头 中,分别(7.2)。

这些是标准规定可以应用对齐规范alignas(...))的唯一位置。请注意,这不包括 typedef 声明或 别名声明
根据 [dcl.attr.grammar] (7.6.1)p4

如果适用于某个实体或语句的 属性说明符序列 包含不允许应用于该实体或语句的 属性,则程序无效。

这种措辞旨在适用于alignas以及可能出现在属性说明符序列中的其他形式的属性,但在从对齐切换为不同类型的属性说明符序列时未正确更新。

因此:您使用alignas的示例代码应该是有问题的。C ++标准目前并没有明确说明这一点,但它也不允许使用,因此它目前会导致未定义行为(因为标准未定义任何行为)。


1
正如我的回答所解释的那样,在C++的对齐模型中,这是没有意义的事情。对齐是类型的基本部分,不能更改。typedef仅为现有类型提供新名称,因此其对齐要求与类型相同。 - Richard Smith
1
嗯,我刚刚查看了我的代码(需要定义对齐类型的代码),发现我没有使用alignas,而是在clang和gcc中使用__attribute__((align(K))),在icpc中使用__declspec(align(K))。因此,这意味着你关于C++的说法是正确的。然而,我不同意你的说法,认为这不是一件有意义的事情——毕竟这些扩展功能确实有用。也许你可以编辑你的答案,使它更清楚地表明这不是typedef的问题,而是将alignas应用于类型的问题。 - Walter
1
请注意,我说的是在C++的对齐模型中这样做是没有意义的。在GCC的模型中,这是有意义的,其中typedef上的对齐会给你一个相同但不同的类型(这在各种方面都是错误和不连贯的,但在常见情况下可以工作)。 - Richard Smith
3
我仍然不同意你在这里强调“typedef”的观点。如果你想使用AVX,必须能够定义一个类型,它是一个包含8个浮点数的数组,对齐到32字节或包含4个双精度浮点数的数组,以相同的方式对齐。这在C ++中是不允许的,因此需要使用扩展功能。typedef的问题仅出现在需要给该类型命名短名称的情况下,但这并不是将对齐应用于类型的例子。 - Walter
如果您想使用标准的功能来定义AVX兼容类型,可以使用struct。但通常情况下,如果您正在使用AVX,则需要使用特定于实现的扩展,例如vector属性。特定于实现的扩展不必遵循C++标准所设定的先例,因此如果他们愿意(并且通常会这样做),它们可以在typedef上允许这些东西。 - Richard Smith
显示剩余2条评论

8
C++11标准草案中(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf)对此有所说明(对齐说明符的形式为 alignas ( assignment-expression )):
7.6.2 对齐说明符 [dcl.align] 1 对齐说明符可以应用于变量或类数据成员,但不得应用于位域、函数参数、catch子句的形式参数(15.3)或使用register存储类说明符声明的变量。对齐说明符还可以应用于类或枚举类型的声明。带有省略号的对齐说明符是一个包扩展。
我找到了这个原始提案(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1877.pdf),其中提到:
对齐说明符不成为类型的一部分,但可以创建具有对齐成员变量的类类型。
并给出了以下示例:
// Wrong attempt: Listing 6)
typedef double align_by<0x10000> hwDoubleVector; // Error!
Void clear(hwDoubleVector &toClear, unsigned size);

看起来在使用 typedef 时是非法的。


2
谢谢你的努力,但这不是我要找的答案。原始提议的文本显然不允许我的构建。但它是来自2005年,不是标准。 - Walter
以防万一,这里是提案的参考链接:http://www.stroustrup.com/C++11FAQ.html#align - iefserge

-1

尝试:

typedef float alignas(16) aligned_block[4];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接