Къде мога да използвам alignas() в C++11?

В опит да стандартизирам кода си и да го направя по-преносим, ​​замених

#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):

Съответният член от стандарта е 7.6.2, по-специално 7.6.2.1

Спецификаторът на подравняване може да се приложи към променлива или към член от данни на клас, но не трябва да се прилага към битово поле, функционален параметър, формалният параметър на клауза catch (15.3) или променлива, декларирана с регистрирайте спецификатор на класа за съхранение. Спецификатор на подравняване може също да се приложи към декларацията на клас или тип изброяване. Спецификатор на подравняване с многоточие е разширение на пакет (14.5.3).

както вече е изкопан от Ред XIII. Въпреки това не съм достатъчно експерт, за да знам какво означава това за моя тест по-горе.

Ако фактът, че clang приема моя атрибут, означава нещо, може би си струва да споменем, че когато се опитвате да използвате директива using вместо typedef, clang също се оплаква. Освен това, противно на изявление в по-ранна версия на този въпрос, gcc не само предупреждава, но наистина игнорира желанието ми за подравняване.


person Walter    schedule 03.04.2013    source източник
comment
Използвате ли опцията gcc -std=c++11?   -  person iefserge    schedule 03.04.2013
comment
@SergIef g++ -std=c++11 -Wextra -Wall -pedantic   -  person Walter    schedule 03.04.2013


Отговори (4)


Не можете да приложите подравняване към typedef. В C++ модела на спецификаторите за подравняване подравняването е неделима част от самия тип и typedef не създава нов тип (само предоставя ново име за съществуващ тип), така че няма смисъл да се прилага спецификатор за подравняване в typedef декларация.

От [dcl.align] (7.6.2)p1:

Спецификатор на подравняване може да се приложи към променлива или към член от данни на клас [...]. Спецификатор на подравняване може също да бъде приложен към декларацията или дефиницията на клас (в спецификатор на разработен тип (7.1.6.3) или class-head (съответно клауза 9) и към декларацията или дефиницията на изброяване (съответно в opaque-enum-declaration или enum-head (7.2 )).

Това са единствените места, където стандартът казва, че може да се приложи спецификатор на подравняване (alignas(...)). Обърнете внимание, че това не включва typedef декларации, нито декларация на псевдоним.

Според [dcl.attr.grammar] (7.6.1)p4:

Ако specifier-seq на атрибут, който принадлежи към някакъв обект или израз, съдържа атрибут, който не е разрешен да се прилага към този обект или израз, програмата е неправилно оформена.

Тази формулировка беше предназначена да се приложи към alignas, както и към другите форми на атрибут, които може да се появят в attribute-specifier-seq, но не беше правилно актуализирана, когато подравняването премина от "истински" атрибут към като различен вид specifier-seq на атрибут.

И така: вашият примерен код, използващ alignas, се предполага, че е неправилно оформен. Стандартът C++ понастоящем не казва изрично това, но също така не разрешава използването, така че вместо това в момента би довело до недефинирано поведение (тъй като стандартът не дефинира никакво поведение за него).

person Richard Smith    schedule 30.11.2015
comment
Както обяснява моят отговор, това не е смислено нещо в модела на подравняване на C++. Подравняването е основна част от типа и не може да се променя. typedef просто дава ново име за съществуващ тип, така че не може да има различно изискване за подравняване от това на типа. - person Richard Smith; 03.12.2015
comment
Хм. Току-що погледнах моя код (този, който изискваше да дефинира подравнен тип) и видях, че не използвам alignas, а __attribute__ ((align(K))) за clang и gcc и __declspec(align(K)) за icpc. Така че това означава, че наистина сте прав за C++. Въпреки това не съм съгласен с твърдението ви, че това не е смислено нещо - след като всички тези разширения правят нещо полезно. Може би можете да редактирате отговора си, за да стане по-ясно, че не е проблем с typedef, а с прилагането на alignas към тип. - person Walter; 03.12.2015
comment
Обърнете внимание, че казах, че това не е смислено нещо в модела на подравняване на C++. Има смисъл в модела на GCC, където подравняването на typedef ви дава същия, но различен тип (това е нарушено и непоследователно по различни начини, но работи в общи случаи). - person Richard Smith; 05.12.2015
comment
Все още не съм съгласен с акцента Ви върху typedef тук. Ако искате да използвате AVX, трябва да можете да дефинирате type, който е масив от 8 плаващи числа, подравнени към 32 байта, или масив от 4 двойни числа, подравнени по същия начин. Това не е позволено с C++ и следователно с разширенията. Проблемът с typedef се появява само поради необходимостта да се даде кратко име на този тип, но това не е приложение за подравняване към тип. - person Walter; 06.12.2015
comment
Ако искате да използвате средствата на стандарта, за да дефинирате AVX-съвместим тип, можете да използвате struct. Но обикновено, ако използвате AVX, ще искате да използвате специфично за внедряване разширение, като например атрибут vector. Специфичните за изпълнение разширения не трябва да следват прецедента, определен от стандарта C++, така че те могат да разрешат такива неща на typedefs, ако искат (и обикновено го правят). - person Richard Smith; 08.12.2015
comment
Ако използвам struct, проблемът остава: не мога да декларирам типа да има определено съответствие със стандартния C++. Мога само да декларирам всеки екземпляр на променлива от този тип да има определено подравняване. IHMO, C++ е повреден/непълен/недостатъчен в това отношение. - person Walter; 09.12.2015
comment
Не виждам как това е несъвместимо с нормалното използване на typedef. Дори когато използвате атрибута, вие не създавате нов тип, вие просто давате име на тип, който вече съществува. Разширението GCC не е специфично за typedefs, можете да го използвате във всеки контекст, където обикновено използвате тип. Можете просто да кажете, че T само по себе си се отнася до типа подравняване по подразбиране и че T alignas(N) се отнася до вече съществуващ различен тип с различно подравняване. - person Joseph Garvin; 27.02.2016

Мисля, че току-що поставихте 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]

2 typedef-name може също да бъде въведено чрез декларация на псевдоним. Идентификаторът след ключовата дума using става typedef-name и незадължителният attribute-specifier-seq след identifier< /em> принадлежи на това typedef-име. Той има същата семантика, както ако беше въведен от спецификатора typedef. [...]

(подчертаването е мое)

Горното е съвсем ясно за using, правилата за typedef са разпределени в няколко параграфа, включително в края на §8.3/1, където намирате:

8.3 Значение на деклараторите [dcl.meaning]

1 [...] Незадължителният attribute-specifier-seq следващ declarator-id принадлежи към обекта че е декларирано.

(отново, ударението е мое)


Актуализация: Горният отговор се концентрира върху къде трябва да се постави alignas, а не върху точното му значение. След като помислих още малко, все още смятам, че горното трябва да е валидно. Обмисли:

7.6.2 Спецификатор на подравняване [dcl.align]

1Спецификатор на подравняване може да бъде приложен към променлива или към член от данни на клас, но не трябва да се прилага към битово поле, функционален параметър, < em>exception-declaration (15.3), или променлива, декларирана с register спецификатора на класа за съхранение. Спецификатор на подравняване може също да бъде приложен към декларацията или дефиницията на клас (в спецификатор на разработен тип (7.1.6.3) или class-head (съответно клауза 9) и към декларацията или дефиницията на изброяване (съответно в opaque-enum-declaration или enum-head (7.2 )). Спецификатор на подравняване с многоточие е разширение на пакет (14.5.3).

Той изброява случаите, в които може ясно да се приложи, и изброява случаите, в които ясно не може да бъде приложен. Примерът на въпроса по-горе не е нито едното, нито другото.

Може също да се твърди, че псевдонимът на типа, създаден от typedef или using, носи спецификацията за подравняване като част от типа с псевдоним. Този псевдоним може да се използва за създаване на променлива и т.н., както е разрешено от 7.6.2p1, но не и за създаване на променлива с register и т.н.

В този смисъл смятам, че спецификаторът на атрибута се прилага (по смисъла на 7.6.2) по отложен начин и по този начин примерът на OP все още трябва да е валиден, когато спецификацията за подравняване е поставена на синтактично правилното място.

person Daniel Frey    schedule 09.04.2013
comment
Както за typedefs, така и за accepted using clang оплаквания: грешка: атрибутът 'alignas' се прилага само за променливи, членове на данни и типове тагове - person jjrv; 03.08.2014
comment
Този отговор е грешен. Съгласно [dcl.align]p1, спецификатор на подравняване може да се приложи само към променлива, член от данни или декларация на клас или изброяване. Това не е нито едно от тези неща, така че е неправилно оформено. - person Richard Smith; 30.11.2015
comment
@RichardSmith Актуализирах отговора си, обяснявайки защо смятам, че дори и с 7.6.2 отговорът е правилен. Предвид дискусиите, може би си струва да отворите DR, за да изясните ситуацията. - person Daniel Frey; 03.12.2015
comment
@DanielFrey Казвате, че и Clang, и GCC са щастливи, което е неправилно. Clang отхвърля всичките ви опити да приложите alignas към тип. Относно вашата актуализация: изводът е, че alignas не може да бъде поставен никъде другаде освен местата, изброени в 7.6.2 (имайте предвид, че списъкът с места, където не може да се приложи, е само премахване на случаи, които иначе биха да бъдат включени в списъка с места, където може да се прилага). Останалите случаи са в най-добрия случай UB (чрез липса на спецификация). - person Richard Smith; 05.12.2015
comment
@DanielFrey Освен това, FWIW, вече поисках отваряне на основен проблем, за да изясня, че това е неправилно оформено. [dcl.attr.grammar]p4's Ако attribute-specifier-seq, който принадлежи към някакъв обект или израз, съдържа атрибут, който е не е позволено да се прилага към този обект или изявление, програмата е неправилно оформена. трябваше да го покрие, но се повреди, когато подравняването беше променено от синтаксис на атрибут към ключова дума. - person Richard Smith; 05.12.2015
comment
@RichardSmith Не помня как и коя версия на Clang тествах преди 2,5 години, може би не съм тествал правилно. Както и да е: Защо е преднамерено да се направи горният UB? Кой има това намерение и защо? Има ли проблем, който не виждам? GCC може да съхранява подравняването в псевдонима на типа и то се прилага, когато се използва. - person Daniel Frey; 05.12.2015
comment
Не е предвидено горният случай да бъде UB, намерението (на комисията C++, когато беше добавена функцията за подравняване) е той да бъде неправилно оформен. Но в момента стандартът не дефинира какво означава, което го прави недефиниран (чрез пропуск). - person Richard Smith; 08.12.2015
comment
И да, това е проблем; Атрибутът за подравняване на GCC не ви дава нов тип, той ви дава счупен тип, където само понякога си спомня, че е имал подравняване. Например, подравняването се загубва, когато типът се предава на шаблон. - person Richard Smith; 08.12.2015

Проект на стандарт C++11 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf казва за това (спецификаторът за подравняване е във формата alignas ( израз за присвояване )):

7.6.2 Спецификатор на подравняване [dcl.align]

1 Спецификаторът за подравняване може да се приложи към променлива или към член от данни на клас, но не трябва да се прилага към битово поле, параметър на функция, формалният параметър на клауза за улавяне (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.

person iefserge    schedule 03.04.2013
comment
+1 за усилията, но това не е отговорът, който търся. Текстът на първоначалното предложение очевидно не би позволил моята конструкция. Но е от 2005 г. и не е стандарта. - person Walter; 03.04.2013
comment
За всеки случай, ето къде се споменава предложението stroustrup.com/C++11FAQ.html #подравняване - person iefserge; 03.04.2013

Опитвам:

typedef float alignas(16) aligned_block[4];
person Red XIII    schedule 03.04.2013