Partager via


Attributs en C++

La norme C++ définit un ensemble commun d'attributs. Elle permet également aux fournisseurs de compilateurs de définir leurs propres attributs dans un espace de noms spécifique. Toutefois, les compilateurs ne sont tenus de reconnaître que les attributs définis dans la norme.

Dans certains cas, les attributs standard chevauchent les paramètres __declspec spécifiques au compilateur. Dans Microsoft C++, vous pouvez utiliser l'attribut [[deprecated]] au lieu de __declspec(deprecated). L'attribut [[deprecated]] est reconnu par tout compilateur conforme. Pour tous les autres paramètres __declspec tels que dllimport et dllexport, il n'existe pas encore d'attribut équivalent, vous devez donc continuer à utiliser la syntaxe __declspec. Les attributs n'affectent pas le système de types et ne modifient pas le sens d'un programme. Les compilateurs ignorent les valeurs d'attributs qu'ils ne reconnaissent pas.

Visual Studio 2017 version 15.3 et suivantes (disponible avec /std:c++17 et suivantes) : Dans la portée d'une liste d'attributs, vous pouvez spécifier l'espace de noms pour tous les noms avec un seul introducteur using :

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

Attributs de la norme C++

En C++11, les attributs constituent un moyen normalisé d'annoter les constructions C++ (y compris, mais sans s'y limiter, les classes, les fonctions, les variables et les blocs) avec des informations supplémentaires. Les attributs peuvent être spécifiques à un fournisseur ou non. Un compilateur peut utiliser ces informations pour générer des messages d'information ou pour appliquer une logique spéciale lors de la compilation du code attribué. Le compilateur ignore 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 entourés de doubles crochets :

[[deprecated]]
void Foo(int);

Les attributs représentent une alternative normalisée aux extensions spécifiques aux fournisseurs telles que les directives #pragma, __declspec() (Visual C++) ou __attribute__ (GNU). Cependant, vous devrez toujours utiliser les constructions spécifiques au fournisseur pour la plupart des besoins. 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 de dépendance des données pour la synchronisation des threads. L'attribut peut être appliqué à un ou plusieurs paramètres, pour spécifier que l'argument passé 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 comporte une dépendance en dehors de la fonction. Le compilateur peut utiliser cette information pour générer un 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'elle pourrait ne pas exister dans les versions futures de l'interface d'une bibliothèque. L'attribut [[deprecated]] peut être appliqué à la déclaration d'une classe, d'un nom de type, 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 élément [[deprecated]], il émet l'avertissement 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 des instructions switch comme un indice pour le compilateur (ou toute personne lisant le code) que le comportement de repli est prévu. Actuellement, le compilateur Microsoft C++ ne produit pas d’avertissement pour le comportement de fourre-tout : cet attribut n’a donc pas d’ effet sur le comportement du compilateur.

[[likely]]

Visual Studio 2019 version 16.6 et suivantes : (Disponible avec /std:c++20 et suivantes.) L'attribut [[likely]] spécifie un indice au compilateur que le chemin de code de l'étiquette ou de l'instruction attribuée est plus susceptible de s'exécuter que les alternatives. Dans le compilateur Microsoft, l'attribut [[likely]] marque les blocs comme « code chaud », ce qui incrémente un score d'optimisation interne. Le score s'accroît davantage lors de l'optimisation de la vitesse, et moins lors de l'optimisation de la taille. Le score net affecte la probabilité d'optimisation de l'inlining, du loop unrolling et de la vectorisation. L'effet de [[likely]] et [[unlikely]] est similaire à l'optimisation guidée par le profil, mais limité à l'unité de traduction actuelle. L'optimisation de la réorganisation des blocs n'est pas encore mise en œuvre pour cet attribut.

[[maybe_unused]]

Visual Studio 2017 version 15.3 et suivantes : (Disponible avec /std:c++17 et suivantes.) L'attribut [[maybe_unused]] spécifie qu'une variable, une fonction, une classe, un typedef, un membre de données non statique, un enum 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ée ultérieurement avec l'attribut et vice-versa. Une entité est considérée comme marquée après l'analyse de sa première déclaration marquée [[maybe_unused]] et pour le reste de l'unité de traduction en cours.

[[nodiscard]]

Visual Studio 2017 version 15.3 et suivantes : (Disponible avec /std:c++17 et suivantes.) Spécifie que la valeur de retour d'une fonction n'est pas destinée à être jetée. L'avertissement C4834 est déclenché, comme le montre 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 lance toujours une exception ou sort. Le compilateur peut adapter ses règles de compilation pour les entités [[noreturn]].

[[unlikely]]

Visual Studio 2019 version 16.6 et suivantes : (Disponible avec /std:c++20 et suivantes.) L'attribut [[unlikely]] spécifie un indice au compilateur que le chemin de code pour l'étiquette ou l'instruction attribuée est moins susceptible de s'exécuter que les alternatives. Dans le compilateur Microsoft, l'attribut [[unlikely]] marque les blocs comme étant du « code froid », ce qui diminue un score d'optimisation interne. Le score est davantage décrémenté lors de l'optimisation de la taille, et moins lors de l'optimisation de la vitesse. Le score net affecte la probabilité d'optimisation de l'inlining, du loop unrolling et de la vectorisation. L'optimisation de la réorganisation des blocs n'est pas encore mise en œuvre pour cet attribut.

Attributs spécifiques à Microsoft

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

<tag> est une chaîne qui spécifie le nom de la règle à supprimer. Le champ facultatif justification vous permet d’expliquer pourquoi un avertissement est désactivé ou supprimé. Cette valeur apparaît dans la sortie SARIF lorsque l’option /analyze:log:includesuppressed est spécifiée. Sa valeur est un littéral de chaîne étroite encodé en UTF-8. L’attribut [[gsl::suppress]] est disponible dans Visual Studio 2022 version 17.14 et versions ultérieures.

L'attribut [[gsl::suppress]] spécifique à Microsoft est utilisé pour supprimer les avertissements des vérificateurs qui appliquent les règles de la bibliothèque de support des directives (GSL) dans le code. Prenons l'exemple de 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", justification: "This attribute suppresses Bounds rule #1")]]
    {
        int* q = p + 1; // GSL warning C26481 suppressed
        p = q--; // GSL warning C26481 suppressed
    }
}

L'exemple soulève les avertissements suivants :

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

  • C26485 (Règle 3 des limites : pas de décomposition du tableau en pointeur).

  • C26481 (Règle 1 des limites : n'utilisez pas l'arithmétique des pointeurs, mais plutôt l'empan).

Les deux premiers avertissements se déclenchent lorsque vous compilez ce code avec l'outil d'analyse de code CppCoreCheck installé et activé. Mais le troisième avertissement ne se déclenche pas à cause de l'attribut. Vous pouvez supprimer l'ensemble du profil des limites en écrivant [[gsl::suppress("bounds")]] sans inclure un numéro de règle spécifique. Les directives de base du C++ sont conçues pour vous aider à écrire un code meilleur et plus sûr. L'attribut suppress permet de désactiver facilement les 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é aux mêmes endroits et de la même manière. La différence est que [[msvc::flatten]] va [[msvc::forceinline_calls]] tous les appels dans la portée à laquelle il est appliqué récursivement, jusqu'à ce qu'il n'y ait plus d'appels. Cela peut avoir des conséquences sur la croissance de la taille du code de la fonction ou sur le 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 entraîne l'heuristique inline à tenter de [[msvc::forceinline]] tous les appels dans cette instruction ou 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 étaient 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 s'applique :

  • La fonction ne peut pas être récursive ; son corps ne doit comporter qu'une instruction de retour avec un static_cast du type de paramètre au type de retour.
  • La fonction ne peut accepter qu'un seul paramètre.
  • L'option de compilation /permissive- est requise. (Les options /std:c++20 et suivantes impliquent /permissive- par défaut).

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

Exemple

Dans cet exemple de code, l'attribut [[msvc::intrinsic]] appliqué à la fonction my_move permet au compilateur de remplacer les appels à la fonction par le cast statique souligné 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 le même usage que [[msvc::forceinline_calls]]. Il peut être placé avant n'importe quelle instruction ou bloc. Plutôt que de forcer l'intégration de tous les appels dans ce bloc, il a pour effet de désactiver l'intégration pour la portée à laquelle il s'applique.

[[msvc::no_tls_guard]]

L'attribut [[msvc::no_tls_guard]] spécifique à Microsoft désactive les contrôles d'initialisation lors du premier accès aux variables locales des threads dans les DLL. Les contrôles sont activés par défaut dans le code construit à l'aide de Visual Studio 2019 version 16.5 et des versions ultérieures. Cet attribut ne s'applique qu'à la variable spécifique qui le suit. Pour désactiver globalement les contrôles, utilisez l'option de compilation /Zc:tlsGuards-.