Attributs en C++

La norme C++ définit un ensemble commun d’attributs. Il permet également aux fournisseurs du compilateur de définir leurs propres attributs dans un espace de noms spécifique au fournisseur. Toutefois, les compilateurs sont uniquement requis pour reconnaître les attributs définis dans la norme.

Dans certains cas, les attributs standard se chevauchent avec les paramètres spécifiques au __declspec compilateur. Dans Microsoft C++, vous pouvez utiliser l’attribut [[deprecated]] au lieu d’utiliser __declspec(deprecated). L’attribut [[deprecated]] est reconnu par n’importe quel compilateur conforme. Pour tous les autres __declspec paramètres tels que dllimport et dllexport, jusqu’à présent, il n’y a pas d’équivalent d’attribut. Vous devez donc continuer à utiliser __declspec la syntaxe. Les attributs n’affectent pas le système de type et ne modifient pas la signification d’un programme. Les compilateurs ignorent les valeurs d’attribut qu’ils ne reconnaissent pas.

Visual Studio 2017 version 15.3 et ultérieure (disponible avec /std:c++17 et versions ultérieures) : dans l’étendue d’une liste d’attributs, vous pouvez spécifier l’espace de noms pour tous les noms avec un seul using introductionur :

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

Attributs standard C++

En C++11, les attributs fournissent un moyen standardisé d’annoter les constructions C++ (y compris, mais pas limitées aux classes, fonctions, variables et blocs) avec des informations supplémentaires. Les attributs peuvent ou non être spécifiques au fournisseur. Un compilateur peut utiliser ces informations pour générer des messages d’information ou appliquer une logique spéciale lors de la compilation du code attribué. Le compilateur ignore tous les attributs qu’il ne reconnaît pas, ce qui signifie que vous ne pouvez pas définir vos propres attributs personnalisés à l’aide de cette syntaxe. Les attributs sont placés entre crochets doubles :

[[deprecated]]
void Foo(int);

Les attributs représentent une alternative standardisée aux extensions spécifiques au fournisseur, telles que #pragma les directives ( __declspec() Visual C++) ou __attribute__ (GNU). Toutefois, vous devez toujours utiliser les constructions propres au fournisseur à la plupart des fins. La norme spécifie actuellement les attributs suivants qu’un compilateur conforme doit reconnaître.

[[carries_dependency]]

L’attribut [[carries_dependency]] spécifie que la fonction propage l’ordre des dépendances de données pour la synchronisation de threads. L’attribut peut être appliqué à un ou plusieurs paramètres pour spécifier que l’argument transmis porte une dépendance dans le corps de la fonction. L’attribut peut être appliqué à la fonction elle-même, pour spécifier que la valeur de retour porte une dépendance hors de la fonction. Le compilateur peut utiliser ces informations pour générer du code plus efficace.

[[deprecated]]

Visual Studio 2015 et versions ultérieures : l’attribut [[deprecated]] spécifie qu’une fonction n’est pas destinée à être utilisée. Ou qu’il n’existe peut-être pas dans les futures versions d’une interface de bibliothèque. L’attribut [[deprecated]] peut être appliqué à la déclaration d’une classe, d’un typedef-name, d’une variable, d’un membre de données non statique, d’une fonction, d’un espace de noms, d’une énumération, d’un énumérateur ou d’une spécialisation de modèle. Le compilateur peut utiliser cet attribut pour générer un message d’information lorsque le code client tente d’appeler la fonction. Lorsque le compilateur Microsoft C++ détecte l’utilisation d’un [[deprecated]] élément, il déclenche l’avertissement du compilateur C4996.

[[fallthrough]]

Visual Studio 2017 et versions ultérieures : (disponible avec /std:c++17 et versions ultérieures.) L’attribut [[fallthrough]] peut être utilisé dans le contexte d’instructions switch comme indicateur pour le compilateur (ou toute personne qui lit le code) que le comportement de la procédure de secours est prévu. Actuellement, le compilateur Microsoft C++ n’avertit pas le comportement de secours. Cet attribut n’a donc aucun effet sur le comportement du compilateur.

[[likely]]

Visual Studio 2019 version 16.6 et ultérieure : (disponible avec /std:c++20 et versions ultérieures.) L’attribut [[likely]] spécifie un indicateur au compilateur que le chemin du code de l’étiquette ou de l’instruction attribuée est plus susceptible d’être exécuté que d’autres. Dans le compilateur Microsoft, l’attribut [[likely]] marque les blocs en tant que « code chaud », ce qui incrémente un score d’optimisation interne. Le score est incrémenté davantage lors de l’optimisation de la vitesse, et pas autant lors de l’optimisation de la taille. Le score net affecte la probabilité d’inlining, de déroulement de boucles et d’optimisations de vectorisation. L’effet et [[unlikely]][[likely]] est similaire à l’optimisation guidée par profil, mais limité dans l’étendue à l’unité de traduction actuelle. L’optimisation de la réorganisation des blocs n’est pas encore implémentée pour cet attribut.

[[maybe_unused]]

Visual Studio 2017 version 15.3 et ultérieure : (disponible avec /std:c++17 et versions ultérieures.) L’attribut [[maybe_unused]] spécifie qu’une variable, une fonction, une classe, un typedef, un membre de données non statique, une énumération ou une spécialisation de modèle peut être intentionnellement inutilisée. Le compilateur n’avertit pas lorsqu’une entité marquée [[maybe_unused]] n’est pas utilisée. Une entité déclarée sans l’attribut peut être redéclaré ultérieurement avec l’attribut et vice versa. Une entité est considérée comme marquée après sa première déclaration marquée [[maybe_unused]] est analysée et pour le reste de l’unité de traduction actuelle.

[[nodiscard]]

Visual Studio 2017 version 15.3 et ultérieure : (disponible avec /std:c++17 et versions ultérieures.) Spécifie que la valeur de retour d’une fonction n’est pas destinée à être dis carte ed. Déclenche l’avertissement C4834, comme illustré dans cet exemple :

[[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]]

L’attribut [[noreturn]] spécifie qu’une fonction ne retourne jamais ; en d’autres termes, elle lève toujours une exception ou des sorties. Le compilateur peut ajuster ses règles de compilation pour les [[noreturn]] entités.

[[unlikely]]

Visual Studio 2019 version 16.6 et ultérieure : (disponible avec /std:c++20 et versions ultérieures.) L’attribut [[unlikely]] spécifie un indicateur pour le compilateur que le chemin d’accès au code de l’étiquette ou de l’instruction attribuée est moins susceptible d’être exécuté que d’autres. Dans le compilateur Microsoft, l’attribut [[unlikely]] marque les blocs comme « code froid », ce qui décrémente un score d’optimisation interne. Le score est décrémenté davantage lors de l’optimisation de la taille, et pas autant lors de l’optimisation de la vitesse. Le score net affecte la probabilité d’inlining, de déroulement de boucles et d’optimisations de vectorisation. L’optimisation de la réorganisation des blocs n’est pas encore implémentée pour cet attribut.

Attributs spécifiques à Microsoft

[[gsl::suppress(rules)]]

L’attribut spécifique [[gsl::suppress(rules)]] à Microsoft est utilisé pour supprimer les avertissements des case activée ers qui appliquent des règles GSL (Guidelines Support Library) dans le code. Par exemple, considérez cet extrait de code :

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

L’exemple déclenche ces avertissements :

  • C26494 (Règle de type 5 : Toujours initialiser un objet.)

  • C26485 (Règle de délimitation 3 : Aucun tableau à la décomposition du pointeur.)

  • C26481 (Règle de délimitation 1 : n’utilisez pas l’arithmétique du pointeur. Utilisez plutôt l’étendue.)

Les deux premiers avertissements se déclenchent lorsque vous compilez ce code avec l’outil d’analyse du code CppCoreCheck installé et activé. Mais le troisième avertissement ne se déclenche pas en raison de l’attribut. Vous pouvez supprimer l’intégralité du profil de limites en écrivant [[gsl::suppress(bounds)]] sans inclure un numéro de règle spécifique. Les instructions de base C++ sont conçues pour vous aider à écrire du code mieux et plus sûr. L’attribut suppress facilite la désactivation des avertissements lorsqu’ils ne sont pas souhaités.

[[msvc::flatten]]

L’attribut [[msvc::flatten]] spécifique à Microsoft est très similaire à [[msvc::forceinline_calls]], et peut être utilisé dans les mêmes emplacements et de la même façon. La différence est que tous les appels dans l’étendue qu’il est appliqué de manière récursive, jusqu’à ce qu’aucun [[msvc::flatten]][[msvc::forceinline_calls]] appel ne soit laissé. Cela peut avoir des conséquences pour la croissance de la taille du code résultante de la fonction ou du débit du compilateur, que vous devez gérer manuellement.

[[msvc::forceinline]]

Lorsqu’il est placé avant une déclaration de fonction, l’attribut [[msvc::forceinline]] spécifique à Microsoft a la même signification que __forceinline.

[[msvc::forceinline_calls]]

L’attribut [[msvc::forceinline_calls]] spécifique à Microsoft peut être placé sur ou avant une instruction ou un bloc. Il provoque la tentative heuristique inline de [[msvc::forceinline]] toutes les appels de cette instruction ou de ce bloc :

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

Le premier appel à foo, et les deux appels à bar, sont traités comme s’ils ont été déclarés __forceinline. Le deuxième appel à foo n’est pas traité comme __forceinline.

[[msvc::intrinsic]]

L’attribut [[msvc::intrinsic]] a trois contraintes sur la fonction à laquelle il est appliqué :

  • La fonction ne peut pas être récursive ; son corps ne doit avoir qu’une instruction return avec un static_cast type de paramètre vers le type de retour.
  • La fonction ne peut accepter qu’un seul paramètre.
  • L’option /permissive- du compilateur est requise. (Les /std:c++20 options ultérieures impliquent /permissive- par défaut.)

L’attribut spécifique à [[msvc::intrinsic]] Microsoft indique au compilateur d’inliner une métafonction qui agit comme un cast nommé du type de paramètre vers le type de retour. Lorsque l’attribut est présent sur une définition de fonction, le compilateur remplace tous les appels à cette fonction par un cast simple. L’attribut [[msvc::intrinsic]] est disponible dans Visual Studio 2022 version 17.5 preview 2 et versions ultérieures. Cet attribut s’applique uniquement à la fonction spécifique qui la suit.

Exemple

Dans cet exemple de code, l’attribut [[msvc::intrinsic]] appliqué à la my_move fonction effectue les appels de remplacement du compilateur à la fonction par le cast statique inline dans son corps :

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::noinline]]

Lorsqu’il est placé avant une déclaration de fonction, l’attribut [[msvc::noinline]] spécifique à Microsoft a la même signification que declspec(noinline).

[[msvc::noinline_calls]]

L’attribut [[msvc::noinline_calls]] spécifique à Microsoft a la même utilisation que [[msvc::forceinline_calls]]. Il peut être placé avant toute instruction ou bloc. Au lieu de forcer l’incorporation de tous les appels dans ce bloc, elle a l’effet de désactiver l’inlining pour l’étendue à laquelle elle est appliquée.

[[msvc::no_tls_guard]]

L’attribut spécifique [[msvc::no_tls_guard]] à Microsoft désactive les case activée pour l’initialisation lors de la première accès aux variables locales de thread dans les DLL. Les case activée sont activées par défaut dans le code généré à l’aide de Visual Studio 2019 version 16.5 et versions ultérieures. Cet attribut s’applique uniquement à la variable spécifique qui la suit. Pour désactiver les case activée globalement, utilisez l’option du /Zc:tlsGuards- compilateur.