Где я могу использовать alignas() в С++ 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];
в С++ 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()
может и не может быть размещен.
EDIT (апрель 2013 г.):
Соответствующей статьей стандарта является 7.6.2, в частности 7.6.2.1
Спецификатор выравнивания может применяться к переменной или к члену данных класса, но он не должен применяться к битовому полю, параметру функции, формальному параметру положения catch (15.3) или переменной объявлен с помощью спецификатора класса хранения регистров. Спецификатор выравнивания может также применяться к объявлению класса или типа перечисления. Спецификатор выравнивания с эллипсисом представляет собой расширение пакета (14.5.3).
как уже было выкопано Red XIII. Тем не менее, я недостаточно эксперт, чтобы узнать, что это значит для моего теста выше.
Если факт, что clang принимает мой атрибут, означает что-нибудь, возможно, стоит упомянуть, что при попытке использовать директиву using
вместо typedef
, clang также жалуется. Кроме того, вопреки утверждению в более ранней версии этого вопроса gcc не только предупреждает, но и игнорирует мое желание выравнивания.
Ответы
Ответ 1
Вы не можете применить выравнивание к typedef
. В модели спецификаторов выравнивания С++ выравнивание является неотъемлемой частью самого типа, а typedef
не создает новый тип (он предоставляет только новое имя для существующего типа), поэтому не имеет смысла применять спецификатор выравнивания в объявлении typedef
.
От [dcl.align] (7.6.2) p1:
Спецификатор выравнивания может применяться к переменной или члену данных класса [...]. Спецификатор выравнивания также может быть применен к объявлению или определению класса (в спецификаторе специфицированного типа (7.1.6.3) или head-head (раздел 9) соответственно), так и к объявлению или определению перечисления ( в непрозрачной декларации или enum-head соответственно (7.2)).
Это единственные места, где стандарт говорит, что может применяться спецификатор выравнивания (alignas(...)
). Обратите внимание, что этот не содержит объявления typedef
и объявления-псевдонимы.
Per [dcl.attr.grammar] (7.6.1) p4:
Если атрибут-спецификатор-seq, который содержит какой-либо объект или оператор, содержит атрибут, который не разрешен для применения к этому объекту или инструкции, программа плохо сформирована.
Эта формулировка предназначена для применения к alignas
, а также к другим формам атрибута, которые могут появляться в атрибуте-спецификаторе-seq, но не были правильно обновлены, когда выравнивание переключилось с "реального" атрибута на то, что оно является другой тип атрибута-спецификатора-seq.
Итак: ваш пример кода с использованием alignas
должен быть плохо сформирован. В настоящее время стандарт С++ явно не говорит об этом, но также не позволяет использовать его, поэтому вместо этого в настоящее время это приведет к поведению undefined (поскольку стандарт не определяет для него никакого поведения).
Ответ 2
Я думаю, вы просто поместили 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 также может быть введено с помощью объявления alias. Идентификатор, следующий за ключевым словом using
, становится typedef-name и необязательным атрибутом-спецификатором-seq, следующим за идентификатором, для этого typedef-name. Он имеет ту же семантику, что и его спецификатор typedef
. [...]
(акцент мой)
Вышеприведенное ясно для using
, правила для typedef
распространяются через несколько абзацев, в том числе в конце §8.3/1, где вы найдете:
8.3 Значение деклараторов [dcl.meaning]
1 [...] Необязательный атрибут-спецификатор-seq , следующий за идентификатором-идентификатором, относится к объявленной сущности.
(опять же, акцент мой)
Обновление: вышеприведенный ответ сконцентрирован на , где должен быть помещен alignas
, а не на его точное значение. Подумав об этом еще, я все еще думаю, что вышеизложенное должно быть верным. Рассмотрим:
7.6.2. Спецификатор выравнивания [dcl.align]
1 Спецификатор выравнивания может применяться к переменной или к элементу данных класса, но он не должен применяться к битовому полю, параметру функции, объявлению исключения (15.3 ) или переменная, объявленная с помощью спецификатора класса register
. Спецификатор выравнивания также может быть применен к объявлению или определению класса (в спецификаторе специфицированного типа (7.1.6.3) или head-head (раздел 9) соответственно), так и к объявлению или определению перечисления ( в непрозрачной декларации или перечислении, соответственно (7.2)). Спецификатор выравнивания с эллипсисом представляет собой расширение пакета (14.5.3).
В нем перечислены случаи, когда он может быть четко применен, и перечисляет случаи, когда это явно не может быть применено. Вышеупомянутый вопрос не является ни тем.
Можно также утверждать, что псевдоним типа, созданный typedef
или using
, несут спецификацию выравнивания как часть псевдонимов. Этот псевдоним может быть использован для создания переменной и т.д., Как разрешено 7.6.2p1, но не для создания переменной с register
и т.д.
В этом смысле я считаю, что спецификатор атрибута применяется (в смысле 7.6.2) отложенным образом, и поэтому пример OPs должен оставаться действительным, когда спецификация выравнивания помещается в синтаксически правильное место.
Ответ 3
Проект С++ 11 standard 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
.
Ответ 4
Try:
typedef float alignas(16) aligned_block[4];