В C ++ 11 атрибуты были добавлены как способ к стандартизированным функциям, таким как gnu __attribute__ и msvc's __declspec.

Предоставление языка для стандартных атрибутов, а также нестандартных атрибутов посредством использования пространств имен, хотя поведение нестандартных атрибутов было урегулировано только для C ++ 17. И, к сожалению, по состоянию на 2018 год ни GCC, ни MSVC не предлагают атрибуты, зависящие от поставщика, хотя переносимый стандартный синтаксис C ++.

Большинство стандартных атрибутов были добавлены в C ++ 14 и 17. Список можно найти на cppreference.

В какой-то момент до выхода C ++ 11 черновик стандарта C ++ определял атрибуты [[override]] и [[final]]. Позже эти функции были преобразованы в контекстные ключевые слова override и final. Первоначальное предложение даже предполагало, что методы по умолчанию и удаленные методы могут быть сделаны возможными с использованием атрибутов. Теперь мы знаем эти функции как =default и =delete.

И с тех пор, когда добавляется языковая функция в C ++, необходимо задавать и отвечать на вопрос, будет ли эта функция лучше обслуживаться ключевым словом или атрибутом.

Недавно он подошел к теперь принятому [[no_unique_address]] и обсуждаемому в настоящее время [[move_relocates]] (который, насколько я понимаю, был бы сродни деструктивному ходу, в некоторых случаях хорошей оптимизации производительности).

Член комитета систематически указывает, что «компилятор должен иметь право игнорировать атрибуты». Еще один добавит: «Ну, вообще-то атрибут не должен изменять семантику программы». Или, точнее,

Компиляция действующей программы со всеми игнорируемыми экземплярами определенного атрибута должна привести к правильной реализации исходной программы.

источник: p0840r0

Это своего рода неписаное правило, его фактически нет нигде в стандарте, хотя я нашел эту цитату в первоначальном предложении атрибутов:

Что делает его хорошим кандидатом для атрибутов, так это то, что код, который работает с этими атрибутами, также работает идентично, если атрибуты игнорируются, хотя и с меньшим количеством проверок типов.

Это имеет смысл в случае нестандартных атрибутов. Мы должны оставить позади темное прошлое непереносимого кода и расширений поставщиков. Поэтому просто пропускать нестандартные атрибуты очень полезно и желательно.

А как насчет стандартных атрибутов? Компиляторы также могут игнорировать их.

Итак, допустим, вы используете атрибут [[fallthrough]] и имеете политику нулевых предупреждений (скажем, вы компилируете с -WError), компилятор может выдать предупреждение и завершить сборку.

В случае [[no_unique_address]], sizeof может возвращать другой результат в зависимости от того, игнорируется атрибут или нет, тем самым позволяя вам влиять на семантику программы. И то, и другое показывает, что комитет не обязательно следует своим собственным правилам, но, что наиболее важно, игнорирование атрибута не соответствует намерениям разработчика.

Даже когда люди привыкли ожидать, что поставщики часто внедряют стандарт частичным и самоуверенным образом, они, вероятно, не присыпают атрибуты просто ради удовольствия или не украшают свой код какими-нибудь вычурными рождественскими украшениями. Если кто-то затрудняется пометить функцию [[nodiscard]], он, вероятно, действительно хочет, чтобы результат функции был проверен. Возможно, невыполнение проверки возвращаемого значения по какой-то причине может привести к критическому сбою. Взрываются ракеты, умирают пациенты.

Для существующих атрибутов также нет проблем с реализуемостью. Все они могут быть реализованы на любом оборудовании, поскольку не предъявляют требований к оборудованию. Я полагаю, что есть какое-то оборудование, для которого [[caries_dependencies]] не имеет смысла, но на таком оборудовании std::memory_order тоже не имело бы смысла, что делает вопрос спорным.

Я пытался задать нескольким членам комитета назойливый вопрос: Почему? Почему атрибуты не должны иметь семантического значения?

Я получил ответ: Потому что

И мне было трудно найти более обоснованное объяснение этому. И, как правило, нет ничего утомительного без обоснования.

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

Это, безусловно, серьезное беспокойство. Однако действительно ли комитету нужно навязывать себе правила? Им по-прежнему нужно голосовать по каждому стандартному атрибуту, который входит в стандарт, и необходимо изучить релевантность атрибута с или без существования этого странного неписаного правила.

Я не думаю, что существует хороший общий ответ на вопрос, должны ли атрибуты быть ключевыми словами и должны ли они иметь семантическое значение или нет.

Вместо этого на этот вопрос нужно отвечать для каждого атрибута. Должно ли alignas быть ключевым словом? Может быть нет. Должен [[fallthrough]] быть им? Наверное; это хороший пример использования ключевого слова в качестве атрибута, чтобы избежать нарушения пользовательского кода.

В конечном счете, такого рода соображения весьма субъективны, но я сомневаюсь, что введение кажущихся произвольными правил упрощает процесс проектирования, скорее наоборот.

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

Это может звучать как препирательство, и в некоторой степени это так. Однако это может иметь значение в контексте размышлений.

Отражение атрибутов

Я думаю, что размышления об атрибутах могут быть самым важным аспектом рефлексии. Допустим, вы хотите сериализовать класс и, используя отражение для посещения всех членов, вам может потребоваться отфильтровать некоторые члены, которые вы не хотите сериализовать. Один из способов сделать это - (ab) использовать систему типов, но лучшей системой, вероятно, было бы использование атрибутов для маркировки соответствующих элементов. Это может открыть дверь к потрясающей сериализации, библиотекам RCP и ORM (… хотя вам, вероятно, не следует использовать ORM!)

Большинство людей, кажется, понимают ценность такой функции, однако некоторые утверждают, что было бы лучше добавить другой синтаксис, который можно было бы назвать декоратором . По сути, это будет то же самое, что и атрибуты, используемые там же, где и атрибуты, но с новым синтаксисом, который будет исключен из споров «атрибут должен быть игнорируемым».

Для меня это действительно не имеет смысла. Во-первых, если пользователь решает придавать семантическое значение атрибутам посредством отражения, тогда это выбор пользователя, а не компилятора, поэтому проблемы переносимости не применяются. И, конечно же, эта функция может и будет использоваться для разработки специфичного для фреймворка поведения и идиом, против которых некоторые люди, по-видимому, весьма категорически возражают. Но это потребность, которая существует и эмулируется сегодня с помощью сложных макросов или генерации кода, часто и того, и другого (Qt).

Добавление нового синтаксиса, присвоение ему другого имени из-за некоторой философской тонкости, которая будет потеряна для неспециалистов, почти наверняка не добавит ценности языку. И, конечно, добавит задержки, пока обсуждают этот новый синтаксис. В C ++ заканчиваются токены.

Например, что из перечисленного вы считаете наиболее читаемым?

[[deprecated]] QSignal<void> f();
[[deprecated]] @@qt::signal@@ void f();
[[deprecated]] [[qt::signal]] void f();

Есть и другие проблемы с отражением атрибутов. Атрибуты могут принимать параметры, которые в настоящее время определены как суп из токенов. Хм, я имею в виду последовательность токенов, извините. Хорошо сбалансированная последовательность токен-суп. Что, я думаю, имеет смысл для инструментов и компиляторов. Но это довольно бессмысленно, когда дело касается размышлений.

Я думаю, что есть решения этой проблемы, а именно: отражаемые атрибуты объявляются в источнике перед использованием и разрешают литералы и constexptr в качестве их параметров, а не произвольные токены.

Я разместил более подробную информацию об этом на GitHub. Обратите внимание, что это не предложение, и я не собираюсь его делать, по крайней мере, пока существует мантра «атрибут должен быть игнорируемым».



В конце концов, атрибуты могут иметь множество применений:

  • Директивы компилятора для оптимизации и диагностики
  • Инструкции и метаданные для внешних инструментов
  • Фильтрация и оформление с использованием как отражения инъекции кода (инъекция кода, позволила бы людям использовать атрибуты как полномасштабные декораторы, аналогичные по выразительности одноименной функции, предлагаемой Python).
  • Контракты ([[expects:]]и [[ensures:]]); хотя эти синтаксисы достаточно различаются, поэтому я не уверен, что они по-прежнему квалифицируются как атрибуты.

Но сейчас я чувствую, что атрибуты преступно недооцениваются и искалечены.

Какие атрибуты вы хотели бы видеть?