Quelles sont les règles d'utilisation d'un trait de soulignement dans un identifiant C ++?

931

Il est courant en C ++ de nommer les variables membres avec une sorte de préfixe pour indiquer le fait que ce sont des variables membres, plutôt que des variables ou des paramètres locaux. Si vous venez d'un arrière-plan MFC, vous utiliserez probablement m_foo. J'en ai aussi vu myFoooccasionnellement.

C # (ou peut-être juste .NET) semble recommander d'utiliser juste un trait de soulignement, comme dans _foo. Est-ce autorisé par la norme C ++?

Roger Lipscombe
la source
3
La page de manuel de la glibc à ce sujet peut être trouvée sur gnu.org/software/libc/manual/html_node/Reserved-Names.html Edit: voir aussi opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.html
CesarB
6
Juste pour noter que l'ignorance de ces règles n'implique pas nécessairement que votre code ne sera pas compilé ou exécuté, mais il est probable que votre code ne sera pas portable sur différents compilateurs et versions, car il ne peut pas être garanti qu'il n'y aura pas de nom affrontements. Pour étayer cela, je connais une certaine implémentation d'un système important qui a utilisé comme convention de dénomination la lettre majuscule partout. Il n'y avait aucune erreur à cause de cela. Bien sûr, c'est une mauvaise pratique.
g24l

Réponses:

852

Les règles (qui n'ont pas changé en C ++ 11):

  • Réservé dans n'importe quelle portée, y compris pour une utilisation en tant que macros d' implémentation :
    • identificateurs commençant par un trait de soulignement suivi immédiatement d'une lettre majuscule
    • identifiants contenant des traits de soulignement adjacents (ou "double trait de soulignement")
  • Réservé dans l'espace de noms global:
    • identificateurs commençant par un trait de soulignement
  • De plus, tout dans l' stdespace de noms est réservé. (Vous êtes cependant autorisé à ajouter des spécialisations de modèle.)

De la norme C ++ 2003:

17.4.3.1.2 Noms globaux [lib.global.names]

Certains ensembles de noms et de signatures de fonctions sont toujours réservés à l'implémentation:

  • Chaque nom qui contient un double trait de soulignement ( __) ou commence par un trait de soulignement suivi d'une lettre majuscule (2.11) est réservé à l'implémentation pour toute utilisation.
  • Chaque nom qui commence par un trait de soulignement est réservé à l'implémentation pour être utilisé comme nom dans l'espace de noms global. 165

165) Ces noms sont également réservés dans l'espace de noms ::std(17.4.3.1).

Parce que C ++ est basé sur la norme C (1.1 / 2, C ++ 03) et C99 est une référence normative (1.2 / 1, C ++ 03), ils s'appliquent également, à partir de la norme C 1999:

7.1.3 Identifiants réservés

Chaque en-tête déclare ou définit tous les identifiants répertoriés dans son sous-paragraphe associé, et déclare ou définit facultativement les identifiants répertoriés dans le sous-paragraphe et les identifiants des directions de bibliothèque qui lui sont associés, qui sont toujours réservés pour toute utilisation ou pour être utilisés comme identificateurs de portée de fichier.

  • Tous les identifiants qui commencent par un trait de soulignement et soit une lettre majuscule ou un autre trait de soulignement sont toujours réservés à toute utilisation.
  • Tous les identifiants qui commencent par un trait de soulignement sont toujours réservés pour être utilisés comme identifiants avec une portée de fichier dans les espaces de nom ordinaire et de balise.
  • Chaque nom de macro dans l'un des sous-paragraphes suivants (y compris les futures directions de bibliothèque) est réservé pour une utilisation telle que spécifiée si l'un de ses en-têtes associés est inclus; sauf indication contraire explicite (voir 7.1.4).
  • Tous les identifiants avec liaison externe dans l'un des sous-paragraphes suivants (y compris les futures instructions de bibliothèque) sont toujours réservés pour être utilisés comme identifiants avec liaison externe. 154
  • Chaque identifiant avec portée de fichier répertorié dans l'un des sous-paragraphes suivants (y compris les futures instructions de bibliothèque) est réservé pour être utilisé comme nom de macro et comme identifiant avec portée de fichier dans le même espace de nom si l'un de ses en-têtes associés est inclus.

Aucun autre identifiant n'est réservé. Si le programme déclare ou définit un identifiant dans un contexte dans lequel il est réservé (autre que celui autorisé par 7.1.4), ou définit un identifiant réservé comme nom de macro, le comportement n'est pas défini.

Si le programme supprime (avec #undef) toute définition de macro d'un identifiant dans le premier groupe répertorié ci-dessus, le comportement n'est pas défini.

154) La liste des identificateurs réservés avec une liaison externe comprend errno, math_errhandling, setjmpet va_end.

D'autres restrictions peuvent s'appliquer. Par exemple, la norme POSIX réserve de nombreux identifiants susceptibles d'apparaître en code normal:

  • Les noms commençant par une majuscule Esuivaient un chiffre ou une lettre majuscule:
    • peut être utilisé pour des noms de code d'erreur supplémentaires.
  • Noms commençant par isou tosuivis d'une lettre minuscule
    • peut être utilisé pour des fonctions de test et de conversion de caractères supplémentaires.
  • Noms commençant par LC_suivis d'une lettre majuscule
    • peut être utilisé pour des macros supplémentaires spécifiant des attributs de paramètres régionaux.
  • Noms de toutes les fonctions mathématiques existantes suffixés fou lréservés
    • pour les fonctions correspondantes qui opèrent respectivement sur les arguments float et long double.
  • Les noms qui commencent par SIGsuivis d'une lettre majuscule sont réservés
    • pour des noms de signaux supplémentaires.
  • Les noms qui commencent par SIG_suivis d'une lettre majuscule sont réservés
    • pour des actions de signal supplémentaires.
  • Les noms commençant par str, memou wcssuivis d'une lettre minuscule sont réservés
    • pour des fonctions de chaîne et de tableau supplémentaires.
  • Les noms commençant par PRIou SCNsuivis d'une lettre minuscule ou Xsont réservés
    • pour des macros de spécificateur de format supplémentaires
  • Les noms qui se terminent par _tsont réservés
    • pour les noms de type supplémentaires.

Bien que l'utilisation de ces noms à vos propres fins en ce moment ne puisse pas poser de problème, ils soulèvent la possibilité d'un conflit avec les futures versions de cette norme.


Personnellement, je ne démarre tout simplement pas les identifiants avec des traits de soulignement. Nouvel ajout à ma règle: n'utilisez pas de soulignements doubles n'importe où, ce qui est facile car j'utilise rarement des traits de soulignement.

Après avoir fait des recherches sur cet article, je ne termine plus mes identifiants avec _t car cela est réservé par la norme POSIX.

La règle concernant tout identifiant se terminant par _tm'a beaucoup surpris. Je pense que c'est un standard POSIX (pas encore sûr) à la recherche de clarifications et de chapitres et versets officiels. Ceci provient du manuel GNU libtool , listant les noms réservés.

CesarB a fourni le lien suivant vers les symboles réservés POSIX 2004 et note "que de nombreux autres préfixes et suffixes réservés ... peuvent y être trouvés". Les symboles réservés POSIX 2008 sont définis ici. Les restrictions sont un peu plus nuancées que celles ci-dessus.

Roger Pate
la source
14
Le standard C ++ n'importe pas le C, n'est-ce pas? Ils importent certains en-têtes, mais pas la langue dans son ensemble, ni les règles de dénomination, pour autant que je sache. Mais oui, le _t m'a aussi surpris. Mais comme c'est C, il ne peut s'appliquer qu'aux ns globaux. Doit être sûr d'utiliser _t à l'intérieur des classes comme je l'ai lu
jalf
27
La norme C ++ n'importe pas la norme C. Il fait référence à la norme C. L'introduction de la bibliothèque C ++ dit "La bibliothèque met également à disposition les installations de la bibliothèque C standard". Il le fait en incluant les en-têtes de la bibliothèque C Standard avec les modifications appropriées, mais pas en "l'important". La norme C ++ possède un propre ensemble de règles qui décrit les noms réservés. Si un nom réservé en C doit être réservé en C ++, c'est l'endroit pour le dire. Mais la norme C ++ ne le dit pas. Donc je ne crois pas que les choses réservées en C soient réservées en C ++ - mais je peux très bien me tromper.
Johannes Schaub - litb
8
Voici ce que j'ai trouvé sur le problème "_t": n1256 (C99 TC3) dit: "Les noms typedef commençant par int ou uint et se terminant par _t" sont réservés. Je pense que cela permet toujours d'utiliser des noms comme "foo_t" - mais je pense que ceux-ci sont ensuite réservés par POSIX.
Johannes Schaub - litb
59
Donc 'tolérance' est réservée par POSIX car elle commence par 'à' + une lettre minuscule? Je parie que beaucoup de code enfreint cette règle!
Sjoerd
23
@LokiAstari, " La norme C ++ est définie en termes de norme C. En gros, elle dit que le C ++ est C avec ces différences et ces ajouts. " C'est absurde! C ++ fait uniquement référence à la norme C dans [basic.fundamental] et à la bibliothèque. Si ce que vous dites est vrai, où C ++ dit-il cela _Boolet _Imaginaryn'existe-t-il pas en C ++? Le langage C ++ est défini explicitement, pas en termes de "modifications" en C, sinon la norme pourrait être beaucoup plus courte!
Jonathan Wakely
198

Les règles pour éviter la collision de noms sont à la fois dans la norme C ++ (voir le livre Stroustrup) et mentionnées par les gourous C ++ (Sutter, etc.).

Règle personnelle

Parce que je ne voulais pas traiter de cas et que je voulais une règle simple, j'ai conçu une règle personnelle à la fois simple et correcte:

Lorsque vous nommez un symbole, vous éviterez les collisions avec les bibliothèques de compilation / OS / standard si vous:

  • ne commencez jamais un symbole avec un trait de soulignement
  • ne nommez jamais un symbole avec deux traits de soulignement consécutifs à l'intérieur.

Bien sûr, placer votre code dans un espace de noms unique permet également d'éviter les collisions (mais ne protège pas contre les macros malveillantes)

Quelques exemples

(J'utilise des macros parce qu'elles sont les plus polluantes pour le code des symboles C / C ++, mais cela peut aller du nom de variable au nom de classe)

#define _WRONG
#define __WRONG_AGAIN
#define RIGHT_
#define WRONG__WRONG
#define RIGHT_RIGHT
#define RIGHT_x_RIGHT

Extraits du brouillon C ++ 0x

Du fichier n3242.pdf (je m'attends à ce que le texte standard final soit similaire):

17.6.3.3.2 Noms globaux [global.names]

Certains ensembles de noms et de signatures de fonctions sont toujours réservés à l'implémentation:

- Chaque nom qui contient un double trait de soulignement _ _ ou commence par un trait de soulignement suivi d'une lettre majuscule (2.12) est réservé à l'implémentation pour toute utilisation.

- Chaque nom qui commence par un trait de soulignement est réservé à l'implémentation pour être utilisé comme nom dans l'espace de noms global.

Mais aussi:

17.6.3.3.5 Suffixes littéraux définis par l'utilisateur [usrlit.suffix]

Les identificateurs de suffixes littéraux qui ne commencent pas par un trait de soulignement sont réservés pour une future normalisation.

Cette dernière clause est déroutante, sauf si vous considérez qu'un nom commençant par un trait de soulignement et suivi d'une lettre minuscule serait OK s'il n'était pas défini dans l'espace de noms global ...

paercebal
la source
9
@Meysam: __WRONG_AGAIN__contient deux traits de soulignement consécutifs (deux au début et deux à la fin), donc c'est faux selon la norme.
paercebal
8
@ BЈовић: WRONG__WRONGcontient deux traits de soulignement consécutifs (deux au milieu), donc c'est faux selon la norme
paercebal
2
placer votre code dans un espace de noms unique permet également d'éviter les collisions : mais cela ne suffit toujours pas, car l'identifiant peut entrer en collision avec un mot clé quelle que soit sa portée (par exemple __attribute__pour GCC).
Ruslan
1
Pourquoi y a-t-il un problème d'avoir deux soulignements consécutifs au milieu selon la norme? Les suffixes littéraux définis par l'utilisateur s'appliquent aux valeurs littérales telles que 1234567Lou 4.0f; IIRC ceci fait référence à ohttp: //en.cppreference.com/w/cpp/language/user_literal
Jason S
2
Why is there any problem of having two consecutive underscores in the middle according to the standard?Parce que la norme dit que ceux-ci sont réservés. Ce n'est pas un conseil sur le bon ou le mauvais style. C'est une décision de la norme. Pourquoi ont-ils décidé cela? Je suppose que les premiers compilateurs ont déjà utilisé de telles conventions de manière informelle avant la normalisation.
paercebal
38

Depuis MSDN :

L'utilisation de deux caractères de soulignement séquentiels (__) au début d'un identifiant, ou d'un seul trait de soulignement principal suivi d'une lettre majuscule, est réservée aux implémentations C ++ dans toutes les étendues. Vous devez éviter d'utiliser un trait de soulignement de début suivi d'une lettre minuscule pour les noms avec portée de fichier en raison de possibles conflits avec les identificateurs réservés actuels ou futurs.

Cela signifie que vous pouvez utiliser un seul trait de soulignement comme préfixe de variable membre, à condition qu'il soit suivi d'une lettre minuscule.

Cela est apparemment tiré de la section 17.4.3.1.2 de la norme C ++, mais je ne trouve pas de source originale pour la norme complète en ligne.

Voir aussi cette question .

Roger Lipscombe
la source
2
J'ai trouvé un texte similaire dans n3092.pdf (le brouillon de la norme C ++ 0x) à la section: "17.6.3.3.2 Noms globaux"
paercebal
7
Fait intéressant, cela semble être la seule réponse qui offre une réponse directe et concise à la question.
hyde
9
@hyde: En fait, ce n'est pas le cas, car il ignore la règle de ne pas avoir d'identifiants avec un soulignement de tête dans l'espace de noms global. Voir la réponse de Roger . Je me méfierais beaucoup des citations de documents MS VC en tant qu'autorité sur la norme C ++.
sbi
@sbi Je faisais référence à "vous pouvez utiliser un seul trait de soulignement comme préfixe de variable membre, tant qu'il est suivi d'une lettre minuscule" dans cette réponse, qui répond à la question sur le texte de la question directement et de manière concise, sans se noyer dans un mur de texte.
hyde
5
Tout d'abord, je considère toujours l'absence d'échec que la même règle ne s'applique pas à l'espace de noms global comme un échec. Ce qui est pire, cependant, c'est que les traits de soulignement adjacents sont interdits non seulement au début, mais n'importe où dans un identifiant. Cette réponse ne consiste donc pas simplement à omettre un fait, mais fait en réalité au moins une affirmation activement erronée. Comme je l'ai dit, faire référence aux documents MSVC est quelque chose que je ne ferais que si la question concerne uniquement VC.
sbi
25

Quant à l'autre partie de la question, il est courant de mettre le soulignement à la fin du nom de la variable pour ne pas entrer en conflit avec quoi que ce soit interne.

Je le fais même à l'intérieur des classes et des espaces de noms car je n'ai alors qu'à me souvenir d'une règle (par rapport à "à la fin du nom dans la portée globale et au début du nom partout ailleurs").

Max Lybbert
la source
2

Oui, les traits de soulignement peuvent être utilisés n'importe où dans un identifiant. Je crois que les règles sont les suivantes: az, AZ, _ dans le premier caractère et celles + 0-9 pour les caractères suivants.

Les préfixes de soulignement sont courants dans le code C - un seul soulignement signifie "privé" et les doubles soulignements sont généralement réservés à l'usage du compilateur.

John Millikin
la source
3
Ils sont courants dans les bibliothèques. Ils ne devraient pas être courants dans le code utilisateur.
Martin York
43
Les gens font des bibliothèques C en écriture, vous savez.
John Millikin
7
"Oui, les traits de soulignement peuvent être utilisés n'importe où dans un identifiant." C'est faux pour les identifiants globaux. Voir la réponse de Roger .
sbi