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

Posted

技术标签:

【中文标题】我在哪里可以在 C++11 中使用 alignas()?【英文标题】:Where can I use alignas() in C++11? 【发布时间】:2013-03-25 05:07:18 【问题描述】:

为了标准化我的代码并使其更便携,我替换了

#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() 可以和不能放在哪里。

编辑(2013 年 4 月)

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

对齐说明符可以应用于变量或类数据成员,但不能应用于位字段、函数参数、catch 子句 (15.3) 的形式参数或变量使用寄存器存储类说明符声明。对齐说明符也可以应用于类或枚举类型的声明。带省略号的对齐说明符是包扩展 (14.5.3)。

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

如果 clang 接受我的属性这一事实意味着什么,也许值得一提的是,当尝试使用 using 指令而不是 typedef 时,clang 也会抱怨。此外,与此问题早期版本中的声明相反,gcc 不仅警告,而且确实忽略了我的对齐愿望。

【问题讨论】:

你在使用 gcc -std=c++11 选项吗? @SergIef g++ -std=c++11 -Wextra -Wall -pedantic 【参考方案1】:

我认为您只是将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 适用

7.1.3 typedef 说明符 [dcl.typedef]

2typedef-name 也可以由 alias-declaration 引入。 using 关键字后面的 identifier 变成 typedef-name 后面的 可选 attribute-specifier-seq标识符 属于那个typedef-name。它具有与 typedef 说明符引入的语义相同的语义。 [...]

(强调我的)

以上对于using 来说非常清楚,typedef 的规则分布在几个段落中,包括在 §8.3/1 的末尾,您可以在其中找到:

8.3 声明符的含义[dcl.meaning]

1 [...] 可选的 attribute-specifier-seq 遵循 declarator-id到被声明的实体。

(再次强调我的)


更新:以上答案集中在 哪里 alignas 必须放置,而不是其确切含义。再想一想,还是觉得上面说的应该是成立的。考虑:

7.6.2 对齐说明符 [dcl.align]

1alignment-specifier 可以应用于变量或类数据成员,但不能应用于位域、函数参数、异常声明 (15.3) 或使用register 存储类说明符声明的变量。 alignment-specifier 也可以应用于类的声明或定义(在 elaborated-type-specifier (7.1.6.3) 或 class-head (分别为第 9 条)和枚举的声明或定义(分别在 opaque-enum-declarationenum-head 中 (7.2 ))。带有省略号的 alignment-specifier 是包扩展 (14.5.3)。

它列出了可以明确应用的情况,并列出了明确不能应用的情况。上述问题的例子都不是。

也有人会争辩说typedefusing 创建的类型别名将对齐规范作为别名类型的一部分。此别名可用于创建 7.6.2p1 允许的变量等,但不能使用 register 等创建变量。

从这个意义上说,我认为属性说明符以延迟的方式应用(在 7.6.2 的意义上),因此当对齐说明放在语法正确的位置时,OP 示例应该仍然有效。

【讨论】:

对于 typedef 和使用 clang 的“接受”抱怨:错误:'alignas' 属性仅适用于变量、数据成员和标签类型 这个答案是错误的。根据 [dcl.align]p1,alignment-specifier 只能应用于变量、数据成员或类或枚举的声明。这不是那些东西,所以它的格式不正确。 @DanielFrey 您说“Clang 和 GCC 都很高兴”,这是不正确的。 Clang 拒绝您将 alignas 应用于类型的所有尝试。关于您的更新:这意味着alignas 不能放置在 7.6.2 中列出的地方以外的任何地方(请注意,不能应用它的地方列表删除那些否则将包含在可以应用的地方列表中)。其余情况充其量是 UB(由于缺乏规范)。 @RichardSmith 我不记得我在 2.5 年前如何以及测试了哪个版本的 Clang,也许我没有正确测试。无论如何:为什么打算制作上述 UB?谁有这个意图,为什么?有什么我看不到的问题吗? GCC 可以将对齐“存储”在类型别名中,它是applied when used。 是的,这是个问题; GCC 的对齐属性并没有给你一个新的类型,它给你一个损坏的类型,它有时只记得它有一个对齐。例如,对齐是lost when the type is passed to a template。【参考方案2】:

您不能对typedef 应用对齐方式。在对齐说明符的 C++ 模型中,对齐是类型本身不可分割的一部分,typedef 不会创建新类型(它只是为现有类型提供新名称),因此应用typedef 声明中的对齐说明符。

来自[dcl.align] (7.6.2)p1

对齐说明符可以应用于变量或类数据成员[...]。 alignment-specifier 也可以应用于类的声明或定义(在 elaborated-type-specifier (7.1.6.3) 或 class-head (分别为第 9 条)和枚举的声明或定义(在 opaque-enum-declarationenum-head,分别为 (7.2))。

这些是标准规定可以应用对齐说明符 (alignas(...)) 的唯一地方。请注意,此包括typedef 声明或别名声明

根据 [dcl.attr.grammar] (7.6.1)p4

如果属于某个实体或语句的 attribute-specifier-seq 包含不允许应用于该实体或语句的 属性,则程序有问题-形成。

此措辞旨在适用于alignas 以及可能出现在 attribute-specifier-seq 中的其他形式的属性,但在对齐方式从“真实”属性是一种不同的attribute-specifier-seq

所以:您使用 alignas 的示例代码假定格式错误。 C++ 标准目前没有明确说明这一点,但它也不允许使用,因此目前会导致未定义的行为(因为标准没有为它定义任何行为)。

【讨论】:

正如我的回答所解释的,在 C++ 的对齐模型中,这不是一件有意义的事情。对齐是类型的基本部分,不能更改。 typedef 只是为现有类型提供了一个新名称,因此它不能有与该类型不同的对齐要求。 嗯。我只是查看了我的代码(需要定义对齐类型的代码),发现我没有使用alignas,而是__attribute__ ((align(K))) 用于clang 和gcc,__declspec(align(K)) 用于icpc。所以这意味着你确实对 C++ 是正确的。但是,我不同意你的说法,即这不是一件有意义的事情——毕竟这些扩展做了一些有用的事情。也许您可以编辑您的答案以更清楚地表明这不是typedef 的问题,而是alignas 应用于类型的问题。 请注意,我说过这在C++ 的对齐模型中 是没有意义的。这在 GCC 的模型中是有意义的,typedef 上的对齐会为您提供相同但不同的类型(这在各种方面被破坏和不连贯,但在常见情况下有效)。 我还是不同意你在这里强调typedef。如果您想使用 AVX,您必须能够定义一个 type,它是一个对齐到 32 字节的 8 个浮点数的数组或一个以相同方式对齐的 4 个双精度数的数组。这在 C++ 和扩展中是不允许的。 typedef 的问题只是因为需要给该类型一个短名称而出现,但这不是对类型对齐的应用。 如果你想使用标准的工具来定义一个与 AVX 兼容的类型,你可以使用struct。但通常,如果您使用的是 AVX,则需要使用特定于实现的扩展,例如 vector 属性。特定于实现的扩展不必遵循 C++ 标准设置的先例,因此如果他们愿意(通常会这样做),他们可以在 typedefs 上允许此类事情。【参考方案3】:

C++11 标准草案 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf 对此进行了说明(对齐说明符的形式为 alignas(赋值表达式)):

7.6.2 对齐说明符 [dcl.align]

1 对齐说明符可以应用于变量或类数据成员,但不应应用于 到位域、函数参数、catch 子句 (15.3) 的形式参数或声明的变量 使用寄存器存储类说明符。对齐说明符也可以应用于声明 类或枚举类型。带有省略号的对齐说明符是包扩展。

我找到了这个原始提案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 一起使用是非法的。

【讨论】:

努力+1,但这不是我正在寻找的答案。原始提案的文本显然不允许我进行解释。但它是从 2005 年开始的,它不是标准。 以防万一,这里是提案引用stroustrup.com/C++11FAQ.html#align【参考方案4】:

试试:

typedef float alignas(16) aligned_block[4];

【讨论】:

以上是关于我在哪里可以在 C++11 中使用 alignas()?的主要内容,如果未能解决你的问题,请参考以下文章

我在哪里可以找到 Objective C 的通用游戏状态单例?

在 C++11 中具有对齐元素的 std::array 类型

我在哪里可以找到合适格式(不是 .html)的 C/C++ 用户日志存档?

创建 alignas(64) 整数的向量

内存对齐:如何使用 alignof / alignas?

C++11 中的动态对齐内存分配