Where can I use alignas() in C++11?
Asked Answered
E

4

37

In an effort to standardize my code and make it more portable, I replaced

#ifdef __GNUC__
typedef __attribute__((aligned(16))) float aligned_block[4];
#else
typedef __declspec(align(16)) float aligned_block[4];
#endif

with

typedef float alignas(16) aligned_block[4];

in C++11. However, gnu (4.8) doesn't like that but complains

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

whereas clang 3.2 creates no warning (even with -Weverything -Wno-c++98-compat -pedantic). So I wonder whether my code above is correct and, more generally, where alignas() can and cannot be placed.

EDIT (Apr 2013):

The relevant article from the standard is 7.6.2, in particular 7.6.2.1

An alignment-specifier may be applied to a variable or to a class data member, but it shall not be applied to a bit-field, a function parameter, the formal parameter of a catch clause (15.3), or a variable declared with the register storage class specifier. An alignment-specifier may also be applied to the declaration of a class or enumeration type. An alignment-specifier with an ellipsis is a pack expansion (14.5.3).

as already dug out by Red XIII. However, I'm not expert enough to know what this means for my test above.

If the fact that clang accepts my attribute means anything, it's perhaps worth mentioning that when trying to use a using directive instead of a typedef, clang also complains. Also, contrary to a statement in an earlier version of this question, gcc does not only warn, but indeed ignores my wish for alignment.

Eolande answered 3/4, 2013 at 13:39 Comment(2)
Are you using gcc -std=c++11 option?Porty
@SergIef g++ -std=c++11 -Wextra -Wall -pedanticEolande
E
19

You cannot apply an alignment to a typedef. In the C++ model of alignment specifiers, the alignment is an inseparable part of the type itself, and a typedef does not create a new type (it only provides a new name for an existing type) so it is not meaningful to apply an alignment specifier in a typedef declaration.

From [dcl.align] (7.6.2)p1:

An alignment-specifier may be applied to a variable or to a class data member [...]. An alignment-specifier may also be applied to the declaration or definition of a class (in an elaborated-type-specifier (7.1.6.3) or class-head (Clause 9), respectively) and to the declaration or definition of an enumeration (in an opaque-enum-declaration or enum-head, respectively (7.2)).

These are the only places where the standard says an alignment-specifier (alignas(...)) may be applied. Note that this does not include typedef declarations nor alias-declarations.

Per [dcl.attr.grammar] (7.6.1)p4:

If an attribute-specifier-seq that appertains to some entity or statement contains an attribute that is not allowed to apply to that entity or statement, the program is ill-formed.

This wording was intended to apply to alignas as well as the other forms of attribute that may appear within an attribute-specifier-seq, but was not correctly updated when alignment switched from being a "real" attribute to being a different kind of attribute-specifier-seq.

So: your example code using alignas is supposed to be ill-formed. The C++ standard does not currently explicitly say this, but it also does not permit the usage, so instead it currently would result in undefined behavior (because the standard does not define any behavior for it).

Ezekielezell answered 30/11, 2015 at 19:44 Comment(7)
As my answer explains, that is not a meaningful thing to do in C++'s model of alignment. The alignment is a fundamental part of the type and cannot be changed. A typedef merely gives a new name for an existing type, so it cannot have a different alignment requirement from that of the type.Ezekielezell
Hmm. I just looked at my code (the one that required to defined a aligned type) and see that I don't use alignas, but __attribute__ ((align(K))) for clang and gcc and __declspec(align(K)) for icpc. So this implies that indeed you're correct about C++. However, I don't agree with your statement that this is not a meaningful thing to do -- after all those extensions do something useful. Perhaps you can edit your answer to make it clearer that it's not an issue with the typedef, but with the application of alignas to a type.Eolande
Note that I said this is not a meaningful thing to do in C++'s model of alignment. It is meaningful in GCC's model, where alignment on a typedef gives you a same-but-different type (this is broken and incoherent in various ways, but works in common cases).Ezekielezell
I still disagree with your emphasis of typedef here. If you want to use AVX, you must be able to define a type that is an array of 8 floats aligned to 32bytes or an array of 4 doubles aligned the same way. That is not allowed with C++ and hence the extensions. The issue with the typedef only appears because of the need to give that type a short name, but it's not an application of alignment to a type.Eolande
If you want to use the standard's facilities to define an AVX-compatible type, you can use a struct. But typically if you're using AVX, you'll want to use an implementation-specific extension such as a vector attribute. Implementation-specific extensions don't have to follow the precedent set by the C++ standard, so they can allow such things on typedefs if they want (and typically do so).Ezekielezell
It I use a struct, the problem remains: I cannot declare the type to have a certain alignment with standard C++. I can only declare any instance of a variable of that type to have a certain alignment. IHMO, C++ is broken/incomplete/insufficient in this respect.Eolande
I fail to see how this is inconsistent with normal usage of typedef. Even when you are using the attribute you are not creating a new type, you are just giving a name to a type that already existed. The GCC extension is not specific to typedefs, you can use it in any context where you would normally use a type. You can simply say that T by itself refers to the type of default alignment, and that T alignas(N) refers to an also already existing different type with different alignment.Impermeable
B
39

I think you just placed the alignas in the wrong position. If you move it directly after the identifier, both GCC and Clang are happy and apply the alignment:

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

this is also true if you use using, where the difference also becomes more apparent. Here are two versions that are not accepted by GCC (warning, alignment ignored):

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

and here's the accepted one:

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

I think that GCC applies

7.1.3 The typedef specifier [dcl.typedef]

2 A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. [...]

(emphasis mine)

The above is quite clear for using, the rules for typedef are spread through several paragraphs, including at the end of §8.3/1, where you find:

8.3 Meaning of declarators [dcl.meaning]

1 [...] The optional attribute-specifier-seq following a declarator-id appertains to the entity that is declared.

(again, emphasis mine)


Update: The above answer concentrated on where the alignas has to be placed, not on its exact meaning. After thinking about it some more, I still think that the above should be valid. Consider:

7.6.2 Alignment Specifier [dcl.align]

1An alignment-specifier may be applied to a variable or to a class data member, but it shall not be applied to a bit-field, a function parameter, an exception-declaration (15.3), or a variable declared with the register storage class specifier. An alignment-specifier may also be applied to the declaration or definition of a class (in an elaborated-type-specifier (7.1.6.3) or class-head (Clause 9), respectively) and to the declaration or definition of an enumeration (in an opaque-enum-declaration or enum-head, respectively (7.2)). An alignment-specifier with an ellipsis is a pack expansion (14.5.3).

It lists cases where it can be clearly applied and it lists cases where it clearly can not be applied. The above question's example is neither.

One could also argue that the type alias created by typedef or using is carrying the alignment specification as part of the aliased type. This alias can than be used to create a variable, etc. as allowed by 7.6.2p1 but not to create a variable with register, etc.

In that sense I think that the attribute specifier is applied (in the sense of 7.6.2) in a deferred way and thus OPs example should still be valid when the alignment specification is put in the syntactically correct place.

Beck answered 9/4, 2013 at 20:48 Comment(9)
For both typedefs and "accepted" using clang complains: error: 'alignas' attribute only applies to variables, data members and tag typesRummel
This answer is wrong. Per [dcl.align]p1, an alignment-specifier can only be applied to a variable, a data member, or a declaration of a class or enumeration. This is none of those things, so it's ill-formed.Ezekielezell
@RichardSmith I updated my answer, explaining why I think that even with 7.6.2 the answer is correct. Given the discussions, it might be worth to open a DR to clarify the situation, though.Beck
@DanielFrey You say "both Clang and GCC are happy", which is incorrect. Clang rejects all your attempts to apply alignas to a type. Regarding your update: the implication is that alignas cannot be placed anywhere other than the places listed in 7.6.2 (note that the list of places where it can't be applied are only removing cases that would otherwise be covered by the list of places where it can be applied). The remaining cases are at best UB (through lack of specification).Ezekielezell
@DanielFrey Also, FWIW, I've already requested a core issue be opened to clarify that this is ill-formed. [dcl.attr.grammar]p4's "If an attribute-specifier-seq that appertains to some entity or statement contains an attribute that is not allowed to apply to that entity or statement, the program is ill-formed." was supposed to cover it, but got broken when alignment was changed from attribute syntax to a keyword.Ezekielezell
@RichardSmith I don't remember how and which version of Clang I tested 2.5 years ago, maybe I didn't test properly. Anyways: Why is it intended to make the above UB? Who has this intention and why? Is there any problem that I fail to see? GCC can "store" the alignment in the type alias and it is applied when used.Beck
It's not intended for the above case to be UB, the intent (of the C++ committee, when the alignment feature was added) is for it to be ill-formed. But right now, the standard does not define what it means, which makes it undefined (through omission).Ezekielezell
And yes, it's a problem; GCC's alignment attribute doesn't give you a new type, it gives you a broken type where it only sometimes remembers that it had an alignment. For instance, the alignment is lost when the type is passed to a template.Ezekielezell
As others have said, this answer is wrong. It's not valid to use alignas in a alias, see the currently accepted answer: https://mcmap.net/q/189060/-where-can-i-use-alignas-in-c-11Partlow
E
19

You cannot apply an alignment to a typedef. In the C++ model of alignment specifiers, the alignment is an inseparable part of the type itself, and a typedef does not create a new type (it only provides a new name for an existing type) so it is not meaningful to apply an alignment specifier in a typedef declaration.

From [dcl.align] (7.6.2)p1:

An alignment-specifier may be applied to a variable or to a class data member [...]. An alignment-specifier may also be applied to the declaration or definition of a class (in an elaborated-type-specifier (7.1.6.3) or class-head (Clause 9), respectively) and to the declaration or definition of an enumeration (in an opaque-enum-declaration or enum-head, respectively (7.2)).

These are the only places where the standard says an alignment-specifier (alignas(...)) may be applied. Note that this does not include typedef declarations nor alias-declarations.

Per [dcl.attr.grammar] (7.6.1)p4:

If an attribute-specifier-seq that appertains to some entity or statement contains an attribute that is not allowed to apply to that entity or statement, the program is ill-formed.

This wording was intended to apply to alignas as well as the other forms of attribute that may appear within an attribute-specifier-seq, but was not correctly updated when alignment switched from being a "real" attribute to being a different kind of attribute-specifier-seq.

So: your example code using alignas is supposed to be ill-formed. The C++ standard does not currently explicitly say this, but it also does not permit the usage, so instead it currently would result in undefined behavior (because the standard does not define any behavior for it).

Ezekielezell answered 30/11, 2015 at 19:44 Comment(7)
As my answer explains, that is not a meaningful thing to do in C++'s model of alignment. The alignment is a fundamental part of the type and cannot be changed. A typedef merely gives a new name for an existing type, so it cannot have a different alignment requirement from that of the type.Ezekielezell
Hmm. I just looked at my code (the one that required to defined a aligned type) and see that I don't use alignas, but __attribute__ ((align(K))) for clang and gcc and __declspec(align(K)) for icpc. So this implies that indeed you're correct about C++. However, I don't agree with your statement that this is not a meaningful thing to do -- after all those extensions do something useful. Perhaps you can edit your answer to make it clearer that it's not an issue with the typedef, but with the application of alignas to a type.Eolande
Note that I said this is not a meaningful thing to do in C++'s model of alignment. It is meaningful in GCC's model, where alignment on a typedef gives you a same-but-different type (this is broken and incoherent in various ways, but works in common cases).Ezekielezell
I still disagree with your emphasis of typedef here. If you want to use AVX, you must be able to define a type that is an array of 8 floats aligned to 32bytes or an array of 4 doubles aligned the same way. That is not allowed with C++ and hence the extensions. The issue with the typedef only appears because of the need to give that type a short name, but it's not an application of alignment to a type.Eolande
If you want to use the standard's facilities to define an AVX-compatible type, you can use a struct. But typically if you're using AVX, you'll want to use an implementation-specific extension such as a vector attribute. Implementation-specific extensions don't have to follow the precedent set by the C++ standard, so they can allow such things on typedefs if they want (and typically do so).Ezekielezell
It I use a struct, the problem remains: I cannot declare the type to have a certain alignment with standard C++. I can only declare any instance of a variable of that type to have a certain alignment. IHMO, C++ is broken/incomplete/insufficient in this respect.Eolande
I fail to see how this is inconsistent with normal usage of typedef. Even when you are using the attribute you are not creating a new type, you are just giving a name to a type that already existed. The GCC extension is not specific to typedefs, you can use it in any context where you would normally use a type. You can simply say that T by itself refers to the type of default alignment, and that T alignas(N) refers to an also already existing different type with different alignment.Impermeable
P
8

Draft C++11 standard http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf says about it (Alignment-specifier is of the form alignas ( assignment-expression )):

7.6.2 Alignment specifier [dcl.align]

1 An alignment-specifier may be applied to a variable or to a class data member, but it shall not be applied to a bit-field, a function parameter, the formal parameter of a catch clause (15.3), or a variable declared with the register storage class specifier. An alignment-specifier may also be applied to the declaration of a class or enumeration type. An alignment-specifier with an ellipsis is a pack expansion.

I found this original proposal http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1877.pdf , it says:

The alignment-specifier does not become part of the type, but it is possible to create a class type with aligned member variable(s).

with this example:

// Wrong attempt: Listing 6)
typedef double align_by<0x10000> hwDoubleVector; // Error!
Void clear(hwDoubleVector &toClear, unsigned size);

Looks like it's illegal to use it with typedef.

Porty answered 3/4, 2013 at 15:35 Comment(2)
+1 for the effort, but this is not the answer I'm looking for. The text of the original proposal would clearly not allow my construction. But it's from 2005 and it's not the standard.Eolande
Just in case, here is where proposal referenced stroustrup.com/C++11FAQ.html#alignPorty
D
-1

Try:

typedef float alignas(16) aligned_block[4];
Depravity answered 3/4, 2013 at 16:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.