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

РЕДАКТИРОВАНИЕ (апрель 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) или заголовке класса (пункт 9 соответственно) и к объявлению или определению перечисления (в opaque-enum-declaration или enum-head соответственно (7.2 )).

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

Согласно [dcl.attr.grammar] (7.6.1)p4:

Если attribute-specifier-seq, относящийся к некоторому объекту или оператору, содержит атрибут, который нельзя применять к этому объекту или оператору, программа имеет неправильный формат.

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

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

person Richard Smith    schedule 30.11.2015
comment
Как поясняет мой ответ, это не имеет смысла в модели выравнивания С++. Выравнивание является фундаментальной частью шрифта и не может быть изменено. typedef просто дает новое имя для существующего типа, поэтому у него не может быть требований к выравниванию, отличных от требований типа. - person Richard Smith; 03.12.2015
comment
Хм. Я только что посмотрел на свой код (тот, который требовал определения выровненного типа) и увидел, что я использую не alignas, а __attribute__ ((align(K))) для clang и gcc и __declspec(align(K)) для icpc. Таким образом, это означает, что вы действительно правы насчет С++. Однако я не согласен с вашим заявлением о том, что это не имеет смысла - ведь все эти расширения делают что-то полезное. Возможно, вы можете отредактировать свой ответ, чтобы было понятнее, что проблема не в 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++, поэтому они могут разрешать такие вещи для typedef, если хотят (и обычно так и делают). - 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-name. Он имеет ту же семантику, как если бы он был введен спецификатором typedef. [...]

(выделено мной)

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

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

1 [...] Необязательный элемент attribute-specifier-seq после идентификатора-декларатора относится к объекту что заявлено.

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


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

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

1Спецификатор выравнивания может применяться к переменной или к элементу данных класса, но не должен применяться к битовому полю, параметру функции, элементу < em>объявление-исключения (15.3) или переменная, объявленная с помощью спецификатора класса хранения register. Спецификатор выравнивания также может применяться к объявлению или определению класса (в уточненном-спецификаторе-типа (7.1.6.3) или заголовке класса (пункт 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, так и для принятого использования 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 говорит об этом (Alignment-specifier имеет вид 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.

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