Un nommage et une mise en page cohérents sont utiles.
En particulier, cela minimise les arguments du type « mon style est meilleur que le vôtre ».
Cependant, il existe de nombreux styles différents et les gens y sont passionnés (pro et con).
De plus, la plupart des projets réels intègrent du code provenant de nombreuses sources; il est donc souvent impossible de standardiser sur un seul style pour tout le code.
Suite à de nombreuses requêtes d’assistance, nous présentons un ensemble de règles que vous pourriez utiliser en l’absence d’idées meilleures, mais l’objectif réel est la cohérence, plutôt qu’un ensemble de règles particulier.
Les IDE et outils peuvent aider (tout comme freiner).
ALL_CAPS uniquement pour les noms de macrovoid comme type d’argumentconstLa plupart de ces règles sont esthétiques et les programmeurs ont des opinions fortes.
Les IDE ont aussi tendance à avoir des valeurs par défaut et une gamme d’alternatives.
Ces règles sont des valeurs par défaut suggérées à suivre, sauf si vous avez des raisons de ne pas le faire.
Nous avons reçu des commentaires indiquant que le nommage et la mise en page sont si personnels et/ou arbitraires que nous ne devrions pas tenter de les « légiférer ». Nous ne « légiférons » pas (voir le paragraphe précédent).
Cependant, nous avons reçu de nombreuses demandes d’un ensemble de conventions de nommage et de mise en page à utiliser lorsqu’il n’y a pas de contraintes externes.
Des règles plus spécifiques et détaillées sont plus faciles à faire respecter.
Ces règles ressemblent fortement aux recommandations du PPP Style Guide, écrit en soutien du livre de Stroustrup Programming: Principles and Practice using C++.
Les compilateurs ne lisent pas les commentaires.
Les commentaires sont moins précis que le code.
Les commentaires ne sont pas mis à jour de façon aussi régulière que le code.
auto x = m * v1 + vv; // multiplier m par v1 et ajouter le résultat à vv
Construisez un programme d’IA qui interprète un texte en anglais familier et vérifiez si ce qui est dit pourrait être mieux exprimé en C++.
Le code indique ce qui est fait, pas ce qui est supposé être fait. Souvent l’intention peut être exprimée plus clairement et plus concisément que l’implémentation.
void stable_sort(Sortable& c)
// trie c dans l'ordre déterminé par <, conserve les éléments égaux (définis par ==) dans
// leur ordre relatif d'origine
{
// ... quite a few lines of non-trivial code ...
}
Si le commentaire et le code ne correspondent pas, il est probable qu’ils soient tous les deux incorrects.
La verbosité ralentit la compréhension et rend le code plus difficile à lire en le dispersant dans le fichier source.
Utilisez un anglais intelligible.
Je pourrais être couramment bilingue français, mais la plupart des programmeurs ne le sont pas ; les responsables de mon code ne le seront peut-être pas.
Évitez le langage SMS et surveillez votre grammaire, ponctuation et capitalisation.
Viser le professionnalisme, pas le « cool ».
impossible.
Lisibilité. Évitement des « erreurs stupides ».
int i;
for (i = 0; i < max; ++i); // bogue qui attend d'arriver
if (i == j)
return i;
Il est généralement une bonne idée d’indiquer la déclaration après if (...), for (...) et while (...) :
if (i < 0) error("negative argument");
if (i < 0)
error("negative argument");
Utilisez un outil.
Si les noms reflètent des types plutôt qu’une fonctionnalité, il devient difficile de changer les types employés pour fournir cette fonctionnalité.
De plus, si le type d’une variable change, le code qui l’utilise doit être modifié.
Minimisez les conversions non voulues.
void print_int(int i);
void print_string(const char*);
print_int(1); // répétitif, correspondance manuelle de type
print_string("xyzzy"); // répétitif, correspondance manuelle de type
void print(int i);
void print(string_view); // fonctionne également sur toute séquence semblable à une chaîne
print(1); // clair, correspondance de type automatique
print("xyzzy"); // clair, correspondance de type automatique
Les noms où les types sont encodés sont soit verbeux soit cryptiques.
printS // affiche un std::string
prints // affiche une chaîne C‑style
printi // affiche un int
L’utilisation de techniques comme la notation hongroise pour encoder un type a été utilisée dans des langages non typés, mais elle est généralement inutile et nuisible dans un langage fortement typé statiquement comme le C++, car ces annotations deviennent obsolètes (les défauts sont semblables aux commentaires et se détériorent comme eux) et elles interfèrent avec une bonne utilisation du langage (utilisez le même nom et la résolution de surcharge).
Certains styles utilisent des préfixes très généraux (non spécifiques à un type) pour indiquer l’usage général d’une variable.
auto p = new User();
auto p = make_unique<User>();
// note : "p" n'est pas utilisé pour dire « pointeur brut vers User »,
// juste pour dire « c’est une indirection »
auto cntHits = calc_total_of_hits(/*...*/);
// note : "cnt" n'est pas utilisé pour encoder un type,
// juste pour dire « c’est un compte de quelque chose »
Ceci n’est pas nuisible et ne relève pas de cette directive car il ne encode pas l’information de type.
Certains styles distinguent les membres des variables locales, et/ou des variables globales.
struct S {
int m_;
S(int m) : m_{abs(m)} { }
};
Ceci n’est pas nuisible et ne relève pas de cette directive car il ne encode pas l’information de type.
Comme le C++, certains styles distinguent les types des non-types.
Par exemple, en capitalisant les noms de type, mais pas les noms de fonctions et de variables.
typename<typename T>
class HashTable { // maps string to T
// ...
};
HashTable<int> index;
Ceci n’est pas nuisible et ne relève pas de cette directive car il ne encode pas l’information de type.
Motif : Plus l’étendue est grande, plus la chance de confusion et de collision inattendue augmente.
double sqrt(double x); // renvoie la racine carrée de x ; x doit être non négatif
int length(const char* p); // renvoie le nombre de caractères dans une chaîne C‑style terminée par zéro
int length_of_string(const char zero_terminated_array_of_char[]) // mauvais : verbeux
int g; // mauvais : variable globale avec un nom cryptique
int open; // mauvais : variable globale avec un nom court, populaire
L’utilisation de p pour un pointeur et de x pour une variable flottante est conventionnelle et non trompeuse dans un champ restreint.
???
Motif : La cohérence dans le choix et le style des noms augmente la lisibilité.
Il existe de nombreux styles et, lorsque vous utilisez plusieurs bibliothèques, vous ne pouvez pas suivre toutes leurs conventions différentes.
Choisissez un style d’entreprise, mais laissez les bibliothèques importées avec leur style original.
Standard ISO, utilisez uniquement des minuscules et des chiffres, séparez les mots avec des underscores :
intvectormy_mapÉvitez les noms d’identificateurs contenant des soulignements doubles __ ou commençant par un soulignement suivi d’une majuscule (par ex. _Throws). Ces identificateurs sont réservés à l’implémentation C++.
Stroustrup : Standard ISO, mais en utilisant la majuscule pour vos propres types et concepts :
intvectorMy_mapCamelCase : mettez la majuscule à chaque mot d’un identifiant composé :
intvectorMyMapmyMapCertains styles mettent la majuscule à la première lettre, d’autres pas.
Soyez cohérent dans l’emploi d’acronymes et de la longueur des identificateurs :
int mtbf {12};
int mean_time_between_failures {12}; // choisissez une convention
ALL_CAPS uniquement pour les noms de macroÉviter de confondre les macros avec les noms qui respectent les règles de portée et de type.
void f()
{
const int SIZE{1000}; // mauvais, utilisez « size » à la place
int v[SIZE];
}
En particulier, cela évite de confondre les macros avec les constantes symboliques non macro (voir aussi Enum.5 : Ne pas utiliser ALL_CAPS pour les énumérateurs)
@TODO-LINK
enum bad { BAD, WORSE, HORRIBLE }; // BAD
ALL_CAPSL’usage des soulignements pour séparer les parties d’un nom est le style original de C et C++ et est utilisé dans la bibliothèque standard C++.
Cette règle est une valeur par défaut à utiliser uniquement si vous avez un choix.
Souvent, vous n’avez pas de choix et devez suivre un style établi pour la cohérence.
Le besoin de cohérence l’emporte sur le goût personnel.
Ceci est une recommandation pour lorsque vous n’avez pas de contraintes ou d’idées meilleures.
Cette règle a été ajoutée après de nombreuses demandes de conseils.
Stroustrup : Standard ISO, mais en utilisant la majuscule pour vos propres types et concepts :
intvectorMy_mapImpossible.
Lisibilité.
Utilisez des séparateurs de chiffres pour éviter de longues chaînes de chiffres
auto c = 299'792'458; // m/s2
auto q2 = 0b0000'1111'0000'0000;
auto ss_number = 123'456'7890;
Utilisez des suffixes littéraux quand une clarification est nécessaire
auto hello = "Hello!"s; // une std::string
auto world = "world"; // une chaîne C‑style
auto interval = 100ms; // en utilisant <chrono>
Les littéraux ne doivent pas être répandus partout dans le code comme “magic constants”,
@TODO-LINK
mais il est toujours une bonne idée de les rendre lisibles où ils sont définis.
Il est facile de commettre une faute de frappe dans une longue chaîne d’entiers.
Marquez les longues séquences de chiffres. Le problème est de définir « long » ; peut‑être 7.
Trop d’espace rend le texte plus grand et distrayant.
#include < map >
int main(int argc, char * argv [ ])
{
// ...
}
#include <map>
int main(int argc, char* argv[])
{
// ...
}
Certains IDE ont leurs propres opinions et ajoutent un espace distrayant.
Ceci est une recommandation pour lorsque vous n’avez pas de contraintes ou d’idées meilleures.
Cette règle a été ajoutée après de nombreuses demandes de conseils.
Nous apprécions l’espacement bien placé comme aide importante à la lisibilité. Ne faites simplement pas trop.
Un ordre conventionnel des membres améliore la lisibilité.
Lors de la déclaration d’une classe utilisez l’ordre suivant
using)Utilisez l’ordre public avant protected avant private.
Ceci est une recommandation pour lorsque vous n’avez pas de contraintes ou d’idées meilleures.
Cette règle a été ajoutée après de nombreuses demandes de conseils.
class X {
public:
// interface
protected:
// fonction non vérifiée pour utilisation par les implémentations de classes dérivées
private:
// détails d'implémentation
};
Parfois, l’ordre par défaut des membres entre en conflit avec le désir de séparer l’interface publique des détails d’implémentation.
Dans de tels cas, les types et fonctions privés peuvent être placés avec les données privées.
class X {
public:
// interface
protected:
// fonction non vérifiée pour utilisation par les implémentations de classes dérivées
private:
// détails d'implémentation (types, fonctions et données)
};
Évitez plusieurs blocs de déclarations d’un même accès (p. ex. public) dispersés parmi des blocs de déclarations avec d’autres accès (ex. private).
class X { // mauvais
public:
void f();
public:
int g();
// ...
}
L’utilisation de macros pour déclarer des groupes de membres conduit souvent à violer n’importe quelle règle d’ordre.
Cependant, l’utilisation de macros rend vague ce qui est exprimé de toute façon.
Marquez les écarts par rapport à l’ordre suggéré. Il y aura beaucoup de vieux codes qui ne suivent pas cette règle.
C’est la mise en page originale de C et C++. Elle préserve bien l’espace vertical. Elle distingue bien les différents constructions du langage (telles que fonctions et classes).
Dans le contexte de C++, ce style est souvent appelé « Stroustrup ».
Ceci est une recommandation pour lorsque vous n’avez pas de contraintes ou d’idées meilleures.
Cette règle a été ajoutée après de nombreuses demandes de conseils.
struct Cable {
int x;
// ...
};
double foo(int x)
{
if (0 < x) {
// ...
}
switch (x) {
case 0:
// ...
break;
case amazing:
// ...
break;
default:
// ...
break;
}
if (0 < x)
++x;
if (x < 0)
something();
else
something_else();
return some_value;
}
Remarquez l’espace entre if et (
Utilisez des lignes séparées pour chaque instruction, les branches d’un if, et le corps d’un for.
Le { pour une class et un struct n’est généralement pas sur une ligne séparée, mais le { pour une fonction l’est.
Capitalisez les noms de vos types définis par l’utilisateur pour les distinguer des types de la bibliothèque standard.
Ne capitalisez pas les noms de fonction.
Si vous voulez appliquer une vérification, utilisez un IDE pour reformat.
Le format C souligne l’utilisation dans les expressions et la grammaire, alors que le format C++ souligne les types.
L’utilisation des expressions ne tient pas pour les références.
T& operator[](size_t); // OK
T &operator[](size_t); // juste étrange
T & operator[](size_t); // indécis
Ceci est une recommandation pour lorsque vous n’avez pas de contraintes ou d’idées meilleures.
Cette règle a été ajoutée après de nombreuses demandes de conseils.
Impossible face à l’histoire.
Lisibilité.
Tout le monde n’a pas d’écrans ou d’imprimantes qui rendent facile de distinguer tous les caractères.
Nous confondons facilement des mots semblables et légèrement mal orthographiés.
int oO01lL = 6; // mauvais
int splunk = 7;
int splonk = 8; // mauvais : splunk et splonk sont facilement confus
???
Lisibilité.
Il est vraiment facile d’omettre une instruction lorsqu’il y a plus sur une ligne.
int x = 7; char* p = 29; // ne pas
int x = 7; f(x); ++x; // ne pas
Facile.
Lisibilité.
Réduire la confusion avec la syntaxe de déclarateur.
Pour plus de détails, voir ES.10.
@TODO-LINK
void comme type d’argumentC’est verbeux et n’est requis que lorsqu’il y a compatibilité C.
void f(void); // mauvais
void g(); // mieux
Même Dennis Ritchie a qualifié void f(void) d’abomination.
Vous pouvez faire un argument pour que l’abomination soit abominable en C quand les prototypes de fonctions étaient rares, afin que bannir :
int f();
f(1, 2, "weird but valid C89"); // hope that f() is defined int f(a, b, c) char* c; { /* ... */ }
avoirait causé de gros problèmes, mais non au 21ᵉ siècle et en C++.
constLa notation conventionnelle est plus familière à plus de programmeurs.
La cohérence dans les grandes bases de code.
const int x = 7; // OK
int const y = 9; // mauvais
const int *const p = nullptr; // OK, pointeur constant vers int constant
int const *const p = nullptr; // mauvais, pointeur constant vers int constant
Nous sommes bien conscients que vous pourriez dire que les exemples « mauvais » sont plus logiques que ceux marqués « OK », mais ils sont plus confus pour beaucoup, en particulier les novices qui se réfèrent à du matériel d’enseignement davantage aux deux exemples « OK ».
Marquez le const utilisé comme suffixe d’un type.
C’est une convention de longue date.
Mais la cohérence est plus importante, donc si votre projet utilise quelque chose d’autre, suivez‑le.
Cette convention reflète un motif d’utilisation commun :
Les en-têtes sont plus souvent partagés avec le C pour compiler en C++ et en C, ce qui utilise généralement des .h,
et il est plus facile de nommer tous les en-têtes .h plutôt que d’avoir des extensions différentes uniquement pour ces en-têtes destinés à être partagés avec le C.
D’un autre côté, les fichiers d’implémentation sont rarement partagés avec le C et devraient donc généralement différer des fichiers .c,
et il est donc généralement préférable de nommer tous les fichiers d’implémentation C++ plutôt que .c.
Les noms particuliers .h et .cpp ne sont pas obligatoires (seulement une valeur par défaut) et d’autres noms sont largement utilisés.
Exemples : .hh, .C, et .cxx. Utilisez ces noms de façon équivalente.
Dans ce document, nous faisons référence aux .h et aux .cpp comme un raccourci pour les fichiers d’en-tête et d’implémentation, même si l’extension réelle pourrait être différente.
Votre IDE (si vous en utilisez un) peut avoir des opinions fortes sur les suffixes.
// foo.h:
extern int a; // une déclaration
extern void foo();
// foo.cpp:
int a; // une définition
void foo() { ++a; }
foo.h fournit l’interface à foo.cpp. Les variables globales sont à éviter.
// foo.h:
int a; // une définition
void foo() { ++a; }
#include <foo.h> deux fois dans un programme et vous obtiendrez une erreur de liaison pour deux violations de la règle d’une seule définition.
.h et .cpp (et équivalents) suivent les règles ci-dessous.