L’arrivée du C++17 – Les attributs

Après la présentation générale de cette nouvelle norme, le premier véritable article sur les fonctionnalités du C++ traite des nouveautés concernant les attributs.

Rappels

Les attributs, introduits dans le langage avec la norme C++11, constituent un moyen d’annoter certains éléments du langage. On peut les voir comme un sur-ensemble des annotations Java et une syntaxe standard pour les extensions de compilateurs.

La syntaxe permet d’annoter des éléments du langages (telles que les variables, les fonctions, les instructions…) en spécifiant un attribut.

Les attributs peuvent recevoir des paramètres,  [[deprecated]] par exemple, permettant de signaler un élément déprécié depuis , autorise l’ajout d’un message en argument :

Dans sa FAQ, Bjarne Stroustrup présente exemple d’attribut qui pourrait être utilisé pour la bibliothèque de programmation parallèle OpenMP permettant de remplacer la syntaxe actuelle à base de pragma.

Les attributs constituent une fonctionnalité jeune, mais nous verrons certainement de nombreux nouveaux attributs, standards ou non, arriver dans les normes à venir.

Les nouveaux attributs

C++11 avait ajouté 2 attributs,  [[noreturn]] et  [[carries_dependency]] , complétés par [[deprecated]] en C++14. Le C++17 propose quant à lui 3 nouveaux attributs :  [[falltrought]][[maybe_unused]] et [[nodiscard]].

L’attribut [[fallthrough]]

L’attribut  [[fallthrough]] est destiné aux instructions switch. L’objectif est de préciser au compilateur qu’une absence de saut du flot de contrôle est volontaire (que ce soit un  break ou un return).

Les compilateurs signalent souvent les fallthroughs, c’est à dire lorsque le programme va passer d’une  case à l’autre sans saut. Si il s’agit en effet d’une erreur de programmation assez classique, il est également possible que celle-ci soit intentionnelle.

[[fallthrough]] indique au compilateur que le fallthrough est intentionnel et ne doit pas être considéré comme une erreur. Il permet ainsi d’éviter un warning de la part du compilateur. Celui-ci doit précéder un  case.

L’attribut [[maybe_unused]]

C++17 propose aussi l’attribut  [[maybe_unused]] permettant de signaler au compilateur qu’une variable peut être inutilisée et qu’il n’y a pas lieu de s’inquiéter. Le compilateur ne signalera pas d’avertissement si la variable est effectivement inutilisée.

Dans l’exemple précédant, honteusement pompé sur cppreference.com, l’attribut est appliqué à plusieurs entités.

Ici, dans le cas d’une compilation en mode release, la macro assert va disparaître et par conséquent la variable b sera inutilisée. Puisque l’attribut [[maybe_unused]] est spécifié, le compilateur ne générera pas d’avertissement.

Par ricochet, les arguments de f seront eux aussi inutilisés, d’où l’application de l’attribut à nouveau. L’attribut est également applicable aux fonctions et aux classes.

Petite précision, un élément est marqué comme [[maybe_unused]] , à partir du moment où le compilateur rencontre une déclaration qui le spécifie comme tel, comme le précise le standard : “A name or entity declared without the  maybe_unused attribute can later be redeclared with the attribute and vice versa. An entity is considered marked after the first declaration that marks it.” ([dcl.attr.unused] N4606).

L’attribut [[nodiscard]]

Un poil différent des deux nouveaux attributs précédents, qui jouent sur le contrôle des avertissements du compilateur, l’attribut  [[nodiscard]] permet de refuser le droit du programmeur d’ignorer le retour d’une fonction.

Assez simplement, une fonction avec retour peut être marquée par  [[nodiscard]] ce qui indique au compilateur que le retour ne devrait pas être ignoré :

L’attribut peut également être appliqué à un type. Dans ce cas, toutes les fonctions retournant ce type sont implicitement marquée [[nodiscard]].

Notez que j’ai tenté de voir si le marquage est hérité, mais il semble que ce ne soit pas le cas (quelqu’un de sérieux aurait vérifié dans la norme, mais bon…), en tout cas pour GCC :

Changements sur les attributs

En plus des trois nouveaux attributs, le C++17 modifie également leur fonctionnement général.

Premièrement, les attributs sur les espaces de nom sont désormais autorisés :

De même, le standard autorise aussi les attributs pour les énumérateurs :

Dernier ajout pour les attributs, la possibilité d’utiliser la directive using pour importer un espace de nom dans une directive d’attribut.

Dans l’exemple précédent, les attributs appartiennent tous à l’espace de nom fs, la directive using permet d’importer l’espace de nom de manière à ne pas avoir à le répéter.

Notez que contrairement au célèbre using namespace , l’import de l’espace de nom ne se fait que dans l’attribut courant, ce qui n’implique pas les problèmes de contamination du code et de conflits comme les usages maladroits d' using namespace.

Le C++17 stipule également que les attributs inconnus doivent être ignorés par le compilateur.

Merci d’avoir lu cet article, et à bientôt 🙂

Références et ressources

%d bloggers like this: