Где я могу использовать 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).

как уже выкопал Red XIII. Однако я не достаточно опытен, чтобы знать, что это значит для моего теста выше.

Если тот факт, что clang принимает мой атрибут, что-то значит, возможно, стоит упомянуть, что при попытке использовать using директива вместо typedef, лязг также жалуется. Кроме того, вопреки утверждению в более ранней версии этого вопроса, gcc не только предупреждает, но и фактически игнорирует мое желание выравнивания.

29

Решение

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

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

Выравнивание спецификатор может применяться к переменной или к члену данных класса […]. Выравнивание спецификатор может также применяться к объявлению или определению класса (в уточненный тип Спецификатор (7.1.6.3) или класс-руководитель (Пункт 9, соответственно) и к объявлению или определению перечисления (в непрозрачное-перечисление декларация
или же перечислимого типа головки, соответственно (7.2)).

Это единственные места, где стандарт говорит Выравнивание спецификатор (alignas(...)) может быть применено. Обратите внимание, что это не включают typedef декларации ни псевдоним декларацииs.

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

Если Атрибут спецификатор-сл что относится к какой-либо сущности или оператора содержит атрибут что не разрешено применять к этому объекту или заявлению, программа является плохо сформированной.

Эта формулировка была предназначена для применения к alignas а также другие формы атрибута, которые могут появляться в пределах Атрибут спецификатор-сл, но не был правильно обновлен, когда выравнивание переключилось с «реального» атрибута на другой вид Атрибут спецификатор-сл.

Итак: ваш пример кода с использованием alignas является предполагаемый быть плохо сформированным. Стандарт C ++ в настоящее время явно не говорит об этом, но он также не разрешает использование, поэтому вместо этого он в настоящее время приведет к неопределенному поведению (поскольку стандарт не определяет никакого поведения для него).

8

Другие решения

Я думаю, что вы только что поместили 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 ЬурейеЕ имя также может быть введен псевдоним декларации. идентификатор следуя using Ключевое слово становится ЬурейеЕ имя и необязательный Атрибут спецификатор-сл следуя идентификатор относится к этому ЬурейеЕ имя. Он имеет ту же семантику, как если бы он был введен typedef спецификатор. […]

(акцент мой)

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

8.3 Значение объявлений [dcl.meaning]

1 […] Необязательный Атрибут спецификатор-сл после описатель-идентификатор относится к объекту, который объявлен.

(опять акцент мой)


Обновление: приведенный выше ответ сосредоточен на где alignas должен быть помещен, а не в его точном значении. Подумав об этом еще немного, я все же думаю, что вышесказанное должно быть в силе. Рассматривать:

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

1 Выравнивание спецификатор может применяться к переменной или к члену данных класса, но не должно применяться к битовому полю, параметру функции, Исключение декларирование (15.3), или переменная, объявленная с register спецификатор класса хранения. Выравнивание спецификатор может также применяться к объявлению или определению класса (в уточненный тип Спецификатор (7.1.6.3) или класс-руководитель (Пункт 9, соответственно) и к объявлению или определению перечисления (в непрозрачное-перечисление декларация или же перечислимого типа головки, соответственно (7.2)). Выравнивание спецификатор с многоточием — расширение пакета (14.5.3).

В нем перечислены случаи, когда он может быть четко применен, и перечислены случаи, когда он может явно не применяться Пример приведенного выше вопроса не является ни тем, ни другим.

Можно также утверждать, что псевдоним типа, созданный typedef или же using несет спецификацию выравнивания как часть псевдонима типа. Этот псевдоним может быть использован для создания переменной и т. Д., Как это разрешено в 7.6.2p1, но не для создания переменной с register, так далее.

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

26

Проект стандарта C ++ 11 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf говорит об этом (Alignment-Specifer имеет вид 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,

6

Пытаться:

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