Поделиться через


Атрибуты в C++

Стандарт C++ определяет общий набор атрибутов. Он также позволяет поставщикам компилятора определять собственные атрибуты в пространстве имен для конкретного поставщика. Однако компиляторы должны распознавать атрибуты, определенные в стандарте.

В некоторых случаях стандартные атрибуты перекрываются с параметрами, определенными компилятором. В Microsoft C++можно использовать атрибут вместо использования . Атрибут распознается любым соответствующим компилятором. Для всех других параметров, таких как и , до сих пор нет эквивалента атрибута, поэтому необходимо продолжать использовать синтаксис. Атрибуты не влияют на систему типов, и они не изменяют смысл программы. Компиляторы игнорируют значения атрибутов, которые они не распознают.

Visual Studio 2017 версии 15.3 и более поздних версий (доступно с /std:c++17 и более поздних версий): в области списка атрибутов можно указать пространство имен для всех имен с одним using вводным элементом:

void g() {
    [[using rpr: kernel, target(cpu,gpu)]] // equivalent to [[ rpr::kernel, rpr::target(cpu,gpu) ]]
    do task();
}

Стандартные атрибуты C++

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

[[deprecated]]
void Foo(int);

Атрибуты представляют стандартизированную альтернативу расширениям для конкретных поставщиков, таким как директивы, (Visual C++) или (GNU). Однако вам по-прежнему потребуется использовать конструкции, относящиеся к поставщику, для большинства целей. В настоящее время стандарт указывает следующие атрибуты, которые должен распознать соответствующий компилятор.

[[carries_dependency]]

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

[[deprecated]]

Visual Studio 2015 и более поздних версий: Атрибут [[deprecated]] указывает, что функция не предназначена для использования. Кроме того, он может не существовать в будущих версиях интерфейса библиотеки. Атрибут может применяться к объявлению класса, имени типа, переменной, нестатическому элементу данных, функции, пространству имен, перечислению, перечислителю или специализации шаблона. Компилятор может использовать этот атрибут для создания информационного сообщения при попытке клиентского кода вызвать функцию. Когда компилятор Microsoft C++ обнаруживает использование элемента, он вызывает предупреждение компилятора C4996.

[[fallthrough]]

Visual Studio 2017 и более поздних версий: (доступно с /std:c++17 и более поздних версий.) Атрибут [[fallthrough]] можно использовать в контексте инструкций switch в качестве указания компилятору (или любому пользователю, считывающему код), который предполагается. Компилятор Microsoft C++ в настоящее время не предупреждает о поведении резервного руководства, поэтому этот атрибут не влияет на поведение компилятора.

[[likely]]

Visual Studio 2019 версии 16.6 и более поздних версий: (доступно с /std:c++20 и более поздних версий.) Атрибут [[likely]] указывает указание компилятору, что путь кода для метки атрибута или инструкции, скорее всего, будет выполняться, чем альтернативные. В компиляторе Майкрософт атрибут помечает блоки как "горячий код", который увеличивает внутреннюю оценку оптимизации. Оценка увеличивается больше при оптимизации скорости, а не столько при оптимизации размера. Чистая оценка влияет на вероятность встраивание, отмену циклов и векторизацию оптимизаций. Эффект и аналогичен оптимизации на основе профилей, но ограничен текущей единицей перевода. Оптимизация переупорядочения блоков еще не реализована для этого атрибута.

[[maybe_unused]]

Visual Studio 2017 версии 15.3 и более поздних версий: (доступно с /std:c++17 и более поздних версий.) Атрибут [[maybe_unused]] указывает, что переменная, функция, класс, типdef, нестатический член данных, перечисление или специализация шаблона могут быть намеренно неиспользуемы. Компилятор не предупреждает, когда сущность, помеченная как не используется. Сущность, объявленная без атрибута, может быть переобъявлена атрибутом и наоборот. Сущность считается помеченной после первого объявления, которое помечено, анализируется и для остальной части текущей единицы перевода.

[[nodiscard]]

Visual Studio 2017 версии 15.3 и более поздних версий: (доступно с /std:c++17 и более поздних версий.) Указывает, что возвращаемое значение функции не предназначено для удаления. Вызывает предупреждение C4834, как показано в следующем примере:

[[nodiscard]]
int foo(int i) { return i * i; }

int main()
{
    foo(42); //warning C4834: discarding return value of function with 'nodiscard' attribute
    return 0;
}

[[noreturn]]

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

[[unlikely]]

Visual Studio 2019 версии 16.6 и более поздних версий: (доступно с /std:c++20 и более поздних версий.) Атрибут [[unlikely]] указывает указание компилятору, что путь кода для метки или инструкции атрибута меньше шансов выполнить, чем альтернативные. В компиляторе Майкрософт атрибут помечает как "холодный код", который уменьшает внутреннюю оценку оптимизации. Оценка уменьшается больше при оптимизации размера, а не столько при оптимизации скорости. Чистая оценка влияет на вероятность встраивание, отмену циклов и векторизацию оптимизаций. Оптимизация переупорядочения блоков еще не реализована для этого атрибута.

Атрибуты, относящиеся к Корпорации Майкрософт

[[gsl::suppress(<tag> [, justification: <narrow-string-literal>])]]

Этот атрибут, представленный в Visual Studio 2022 версии 17.14, подавляет предупреждения от контрольных элементов, которые применяют правила Guidelines Support Library (GSL). Атрибут может применяться к оператору, блоку или объявлению. Он доступен только для C++. Вместо этого используйте код C.

— это строка, указывающая имя правила, которое нужно подавить. Необязательное поле позволяет объяснить, почему предупреждение отключается или подавляется. Это значение отображается в выходных данных формата обмена статическими результатами анализа (SARIF) при указании параметра. Его значением является узкостроковый литерал в кодировке UTF-8. Чтобы создать ФАЙЛ SARIF, используйте параметр компилятора .

Пример:

int main()
{
    int arr[10]; // GSL warning C26494 will be fired
    int* p = arr; // GSL warning C26485 will be fired
    [[gsl::suppress("bounds.1", justification: "This attribute suppresses Bounds rule #1")]]
    {
        int* q = p + 1; // GSL warning C26481 suppressed
        p = q--; // GSL warning C26481 suppressed
    }
}

В этом примере возникают следующие предупреждения:

  • C26494 (правило типа 5: всегда инициализировать объект.)
  • C26485 (правило границ 3: нет массива для распада указателя.)
  • C26481 (правило границ 1. Не используйте арифметическую арифметику указателя. Вместо этого используйте диапазон.)

Первые два предупреждения возникают при компиляции этого кода с помощью средства анализа кода CppCoreCheck, установленного и активированного. Но третье предупреждение не срабатывает из-за атрибута. Вы можете отключить весь профиль границ, записав без включения определенного номера правила. Основные рекомендации по C++ предназначены для создания более эффективного и безопасного кода. Атрибут подавления упрощает отключение предупреждений, если они не нужны.

Выбор между и

Оба и предлагают точный контроль над подавлением предупреждений:

  • [[gsl::suppress]] подавляет только предупреждения, созданные Code Analysis Microsoft C++. Используйте его с проверкой основных рекомендаций C++ , которые можно применить к области или определенному объявлению.
  • можно использовать для любого предупреждения компилятора. Полезно, если необходимо подавлять предупреждение в определенном блоке кода, не изменяя структуру кода значительно.

По возможности используйте [[gsl::suppress]] для подавления предупреждений Code Analysis Microsoft C++.

[[msvc::flatten]]

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

[[msvc::forceinline]]

При размещении перед объявлением функции атрибут , определенный корпорацией Майкрософт, имеет то же значение, что и .

[[msvc::forceinline_calls]]

Атрибут , определенный корпорацией Майкрософт, можно поместить в инструкцию или блок или перед ней. Это приводит к тому, что встроенная эвристика пытается выполнить все вызовы в этом операторе или блоке:

void f() {
    [[msvc::forceinline_calls]]
    {
        foo();
        bar();
    }
    ...
    [[msvc::forceinline_calls]]
    bar();
    
    foo();
}

Первый вызов , и оба вызова , рассматриваются как если бы они были объявлены. Второй вызов не рассматривается как .

[[msvc::intrinsic]]

Атрибут имеет три ограничения для функции, к которую она применяется:

  • Функция не может быть рекурсивной; его текст должен иметь только оператор return с типом параметра до возвращаемого типа.
  • Функция может принимать только один параметр.
  • Требуется параметр компилятора. (Параметры и более поздние варианты подразумевают по умолчанию.)

Атрибут, определенный корпорацией Майкрософт, сообщает компилятору, чтобы встраивать метафункционацию, которая выступает в качестве именованного приведения из типа параметра в возвращаемый тип. Когда атрибут присутствует в определении функции, компилятор заменяет все вызовы этой функции простым приведением. Атрибут [[msvc::intrinsic]] доступен в Visual Studio 2022 версии 17.5 предварительной версии 2 и более поздних версий. Этот атрибут применяется только к определенной функции, следующей за ней.

Пример

В этом примере кода атрибут, примененный к функции, заменяет вызовы функции встроенным статическим приведением в тексте:

template <typename T>
[[msvc::intrinsic]] T&& my_move(T&& t) { return static_cast<T&&>(t); }

void f() {
    int i = 0;
    i = my_move(i);
}

[[msvc::musttail]]

Атрибут , представленный в MSVC Build Tools версии 14.50, является экспериментальным атрибутом только для microsoft x64, который применяет оптимизацию хвостового вызова. При применении к соответствующей инструкции возврата компилятор указывает компилятору выдавать вызов в качестве хвостового вызова. Если компилятор не может выпустить хвостовой вызов, он создает ошибку компиляции. Атрибут принудительно применяет вызов хвоста вместо того, чтобы встраивание функции.

Требования:

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

Пример

Вызовы хвоста — это оптимизация компилятора, которая возможна, когда вызов функции является последним действием, выполняемым перед возвратом. Вместо создания нового кадра стека для вызова функции используется повторно. Это сокращает использование стека и повышает производительность, особенно в рекурсивных сценариях.

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

// compile with /O2
#include <iostream>

int increment(int x)
{
    return x + 1;
}

int incrementIfPositive(int x)
{
    if (x > 0)
    {
        [[msvc::musttail]]
        return increment(x);
	}
    return -1;
}

int main()
{
    int result = incrementIfPositive(42);
    if (result < 0)
    {
        return -1;
    }

    std::cout << result; // outputs 43
    return 0;
}

[[msvc::noinline]]

При размещении перед объявлением функции атрибут , определенный корпорацией Майкрософт, имеет то же значение, что и .

[[msvc::noinline_calls]]

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

[[msvc::no_tls_guard]]

Атрибут, определенный корпорацией Майкрософт, отключает проверку инициализации при первом доступе к локальным переменным потока в БИБЛИОТЕКАх DLL. Проверки включены по умолчанию в коде, встроенном с помощью Visual Studio 2019 версии 16.5 и более поздних версий. Этот атрибут применяется только к определенной переменной, следующей за ней. Чтобы отключить проверки глобально, используйте параметр компилятора .