Подтвердить что ты не робот

Где я могу использовать 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 не только предупреждает, но и игнорирует мое желание выравнивания.

4b9b3361

Ответ 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];