Где я могу использовать 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 не только предупреждает, но и фактически игнорирует мое желание выравнивания.
4 ответа
Вы не можете применить выравнивание к typedef
, В модели спецификаторов выравнивания C++ выравнивание является неотъемлемой частью самого типа, и typedef
не создает новый тип (он предоставляет только новое имя для существующего типа), поэтому не имеет смысла применять спецификатор выравнивания в typedef
декларация.
Из [dcl.align] (7.6.2) p1:
Спецификатор выравнивания может быть применен к переменной или к члену данных класса [...]. Спецификатор выравнивания может также применяться к объявлению или определению класса (в подробном спецификаторе типа (7.1.6.3) или в заголовке класса (раздел 9), соответственно) и к объявлению или определению перечисления (в opaque-enum-декларации или enum-head соответственно (7.2)).
Это единственные места, где стандарт говорит спецификатор выравнивания (alignas(...)
) может применяться. Обратите внимание, что это не включает typedef
декларации или псевдонимы s.
Согласно [dcl.attr.grammar] (7.6.1) p4:
Если атрибут-спецификатор-последовательность, который относится к какому-либо объекту или оператору, содержит атрибут, который нельзя применять к этому объекту или оператору, программа является некорректной.
Эта формулировка была предназначена для применения к alignas
а также другие формы атрибута, которые могут появляться в атрибут-спецификаторе-seq, но не корректно обновлялись, когда выравнивание переключалось с "реального" атрибута на другой тип атрибута-спецификатора-seq.
Итак: ваш пример кода с использованием alignas
должен быть плохо сформирован. Стандарт C++ в настоящее время явно не говорит об этом, но он также не разрешает использование, поэтому вместо этого в настоящее время это приведет к неопределенному поведению (потому что стандарт не определяет никакого поведения для него).
Я думаю, что вы только что поместили 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, а необязательный атрибут-specier-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) или в заголовке класса (раздел 9), соответственно) и к объявлению или определению перечисления (в opaque-enum-декларации или enum-head соответственно (7.2)). Спецификатор выравнивания с многоточием является расширением пакета (14.5.3).
В нем перечислены случаи, когда он может быть четко применен, и перечислены случаи, когда он явно не может быть применен. Пример приведенного выше вопроса не является ни тем, ни другим.
Можно также утверждать, что псевдоним типа, созданный typedef
или же using
несет спецификацию выравнивания как часть псевдонима. Этот псевдоним может быть использован для создания переменной и т. Д., Как это разрешено в 7.6.2p1, но не для создания переменной с register
, так далее.
В этом смысле я думаю, что спецификатор атрибута применяется (в смысле 7.6.2) отложенным способом, и, таким образом, пример OP должен все еще быть действительным, когда спецификация выравнивания помещена в синтаксически правильное место.
Проект стандарта 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
,