Начиная со стандарта c++11 в языке появились так называемые "атрибуты", что это такое и зачем они нужны?
Ответ Атрибуты позволяют задавать дополнительную информацию для различных конструкций языка, таких как типы, переменные, имена, блоки и единицы трансляции. Данная информация в частности может быть использована компилятором для генерации более эффективного кода и предоставления (или наоборот, подавления) предупреждающих сообщений пользователю на уровне конкретных участков кода, а не целой программы или компилируемого файла, как это обеспечивается ключами компиляции. Атрибуты появились в c++11 и впоследствии были несколько расширены. На данный момент существуют следующие стандартные атрибуты: assume carries_dependency deprecated fallthrough likely и unlikely maybe_unused nodiscard noreturn no_unique_address Набор атрибутов может быть расширен каждой конкретной реализацией компилятора, в таком случае его поведение будет описываться отдельно. Неизвестный атрибут будет проигнорирован, но может быть выведено предупреждающее сообщение. Атрибут всегда обрамляется двойными квадратными скобками: [[атрибут]]
Пойдём по порядку: [[assume]] Атрибут (доступен начиная с c++23) применяется к пустой операции . Это интерпретируется как предположение. Аргумент атрибута обязан присутствовать и иметь форму (условное_выражение). Тип выражения интерпретируется как bool, а само выражение не является вычисляемым, т.е. не меняет состояние программы. Если результат выражения интерпретируется как true, то предположение не имеет эффекта, в противном случае - поведение не определено. Предположение позволяет компилятору анализировать форму выражения и использовать этот результат для целей оптимизации программы, но это не является обязательным и может быть проигнорировано. int divide_by_32(int x) { [[assume(x >= 0)]] return x/32 // Машинные инструкции, сгенерированные для деления // могут пропустить обработку отрицательных чисел } int f(int y) { [[assume(++y == 43)]] // `y` не будет увеличен, return y // выражение может быть заменено на return 42 } [[carries_dependency]] Данный атрибут не изменяет смысл программы, но может приводить к генерации более эффективного кода. Атрибут может применяться как к целой функции, так и к её параметрам. Может быть полезен на системах со слабо упорядоченной архитектурой при передаче значения между вычислительными потоками. Самый сложный для понимания атрибут :) Поэтому за дополнительной информацией - на enSO. [[deprecated]] Позволяет отметить сущность устаревшей или небезопасной, но тем не менее пока ещё разрешённой к использованию. Может применяться к объявлению класса, typedef-имени, переменной, нестатическому члену данных, функции, пространству имён, перечислению, элементу перечисления или специализации шаблона. Атрибут может быть снабжен аргументом, заданным строковым литералом. Например: [[deprecated("используйте функцию g()")]] void f() Текстовое сообщение будет использовано как подсказка при выводе соответствующего предупреждения. [[fallthrough]] Данный атрибут применяется к пустой операции, т.е. . Может быть использован только внутри switch для уведомления компилятора о задуманном программистом "проваливании" цепочки действий из одной ветки case в другую. Атрибут позволяет подавлять предупреждение компилятора, которое он может выдать, если между метками case не будет обнаружен оператор break. Часто отсутствие break может быть банальной ошибкой, возникшей по невнимательности. Пример: switch (i) { case 1: ... [[fallthrough]] case 2: ... } [[likely]] и [[unlikely]] Атрибуты могут быть добавлены к меткам case или операторам (statement) для подсказки компилятору, что тот или иной участок кода ожидается наиболее вероятным (likely) или, наоборот, менее вероятным (unlikely) при выполнении программы. Пример: void g(int) int f(int n) { if (n > 5) [[unlikely]] { // n > 5 маловероятная ветка g(0) return n * 2 + 1 } switch (n) { case 1: g(1) [[fallthrough]] [[likely]] case 2: // n == 2 более вероятное значение g(2) // нежели любое другое n break } return 3 } [[maybe_unused]] Используется для уведомления компилятора о том, что сущность может быть не использована в программе и следует подавлять соответствующее предупреждение. Может применяться к объявлению класса, typedef-имени, переменной, нестатическому члену данных, функции, пространству имён, перечислению или элементу перечисления. Атрибут может быть полезен при наличии отладочного кода, который не будет включён в бинарник при сборке в release режиме. Пример: [[maybe_unused]] void f([[maybe_unused]] bool thing1, [[maybe_unused]] bool thing2) { [[maybe_unused]] bool b = thing1 && thing2 assert(b) } Ранее приходилось использовать приведение к void для подавления возможных предупреждений. [[nodiscard]] Указывает на обязательность использования результата при возврате из функции. Может быть применим как к типу (при объявлении класса или перечисления), так и непосредственно к возвращаемому типу функции. Пример: [[nodiscard]] int f() { return 42 } ... f() // сообщение о том, что результат функции не использован Явное приведение результата к void подавляет действие атрибута: static_cast(f()) // нет предупреждения о не использованном результате Альтернативно можно использовать присваивание std::ignore: #include std::ignore = f() // нет предупреждения о не использованном результате Начиная с C++20 можно использовать дополнительный строковый литерал для пояснения причины наличия атрибута по аналогии с атрибутом [[deprecated]]. [[noreturn]] Говорит о том, что функция не возвращает управление. Может быть применим к объявлению функции. Актуально для функций, которые заканчивают свою работу выкидыванием исключения, выполняют вечный цикл или прерывают выполнение всей программы. Пример: [[noreturn]] void f() { throw "error" } Если функция, помеченная атрибутом [[noreturn]], возвращает управление на одной из веток выполнения, то это приводит к неопределённому поведению. [[no_unique_address]] Указывает на то, что нестатический член данных класса является потенциально-перекрываемым (potentially-overlapping) подобъектом (не может применяться к битовым полям). Это значит что член может иметь общий адрес с другим нестатическим членом данных этого или базового класса, а заполнители, которые обычно вставляются в конец объекта, могут быть использованы для хранения других членов. Пример: template class hash_map { [[no_unique_address]] Hash hasher [[no_unique_address]] Pred pred [[no_unique_address]] Allocator alloc Bucket *buckets // ... public: // ... } Здесь hasher, pred и alloc могут иметь тот же адрес, что и buckets, если соответствующие им типы окажутся пустыми.