[Histoire du C++] Pourquoi le mot-clé `class` n’a plus de raison d’exister 

Article original : [History of C++] Explanation on why the keyword `class` has no more reason to exist | Belay the C++ (belaycpp.com)
Traductrice : Chloé Lourseyre

Introduction de ce nouveau concept : l’Histoire du C++

Il y a quelques mois (au tout début de cette série) je m’intéressais à ce qu’on pouvait faire d’intéressant en C++, quand j’ai réalisé une chose : le mot-clé class n’a, aujourd’hui, pas de raison valable d’exister !

Cela peut sembler un peu brut comme formulation, mais je vais tout détailler au cours de l’article.

Toujours est-il qu’en cherchant la raison pour laquelle le mot-clé class a été créé à la base, j’ai été amenée à me plonger dans le passé du langage. C’était une expérience intéressante et enrichissante.

Ainsi j’ai décidé d’écrire une mini-série qui relatera de l’histoire du C++ et visant à expliquer des concepts qui sont aujourd’hui dans le langage mais qui, en C++20, peuvent sembler obsolètes, étranges ou sujet à débat, en s’appuyant sur leurs origines.

Sources

Pour cette petite série, j’ai trois sources principales:

Notez que Sibling Rivalry: C and C++ et History of C++: 1979-1991 sont tous les deux librement disponibles sur le site de Stroustrup.

Je suis assez contrite du fait de n’avoir trouvé de source intéressante que de la part d’un seul auteur. Certes, personne n’est mieux placé que le créateur du langage pour parler de son histoire, mais j’aurais aimé avoir aussi le recul d’autres auteurs (si vous en connaissez, n’hésitez pas à me l’indiquer !).

Pourquoi le mot-clé class est totalement dispensable en C++20 ?

Aujourd’hui, en C++20, nous avons deux mot-clés qui fonctionnent presque exactement de la même façon : class et struct.

La seule différence entre les deux est que si vous ne spécifiez pas la visibilité (publicprivate ou protected) des membres, ils seront publics pour une struct et privé pour une class. La visibilité par défaut ne se limite pas aux membres, mais s’étend également à l’héritage (par défaut, les struct héritent en public et les class en privé).

Il y a trois raisons pour lesquelles cette petite différence ne mérite pas un mot-clé à part entière1 :

  • En pratique, les modifieurs d’accès par défaut ne sont presque jamais utilisés, d’expérience. La plupart des développeurs préfèrent spécifier les modifieurs d’accès à chaque fois.
  • En 2021, un bon est un code clair. Expliciter le modifieur d’accès est, dans cette optique, bien meilleur que laisser celui par défaut. Ce fait est débattable sur des petits projets, mais quand on commence à développer en grand nombre, il est toujours mieux d’écrire une poignée de caractères en plus pour être sûr que le code soit clair pour tout le monde.
  • Avoir deux mots-clés est plus ambigu qu’un seul. J’ai très souvent discuté avec des développeurs qui pensaient qu’il y avait plus de différences entre les classes et les structures, parfois en me faisant la liste de ces soi-disant différences. S’il n’y avait qu’un seul mot-clé, il n’y aurait pas ce genre de confusion.

D’autre part, j’ai déjà rencontré des gens qui avaient des arguments contraires. Parmis les plus courants :

  • C’est du sucre syntaxique2.
  • Ils utilisent effectivement les modifieurs d’accès implicites3.
  • Il existe une sémantique derrière chacun des mots-clés qui va au-delà des considérations techniques4.

En tout et pour tout, ce que j’essaye de dire est que le C++ serait en pratique identique si on n’avait pas le mot-clé class. Dans l’état d’esprit du C++20, on peut alors se demander « Quel est l’intérêt d’ajouter un mot-clé qui n’est ni nécessaire, ni utile ? ».

Je sais cependant une chose : class est un des plus vieux mots-clés spécifiques au C++. Plongeons-nous dans l’histoire du langage pour mieux comprendre son existence.

Histoire du mot-clé class

Naissance

La première apparition officielle du mot-clé class se trouve dans Classes: An Abstract Data Type Facility for the C language (Bjarne Stroustrup, 1980), qui ne parle pas vraiment de C++, mais du langage qu’on appelle C with Classes (le C avec des classes).

Qu’est-ce que le C with Classes ? Je ne m’étendrai pas en profondeur dessus ici (je compte y dédier un article complet). Il s’agit du prédécesseur direct du C++ et il a vu le jour en 1979. Le but originel de ce langage était d’ajouter de la modularité au langage C en s’inspirant des classes qu’on trouve en Simula5. Au début, ce n’était pas bien plus qu’un outil, nais il a rapidement évolué pour devenir un langage à part entière.

Comme le C with Classes a connu un succès mitigé et qu’il avait besoin d’un support à temps plein, Bjarne Stroustrup décida d’abandonner ce langage pour en créer un nouveau, visant à être populaire, en utilisant l’expérience qu’il avait acquise en créant le C with Classes. Il appela ce nouveau langage C++.

Le choix du mot-clé class vient directement du Simula et du fait que Stroustrup n’aime pas inventer de nouvelle termiologie.

Vous pouvez en apprendre plus sur le C with Classes dans le livre The Design and Evolution Of C++ (Bjarne Stroustrup), où une section entière lui est dédiée.

Donc le mot-clé class est en réalité né du prédécesseur du C++. En terme de design, il s’agit du plus vieux concept du langage et même de la raison de sa création.

Difference originelle entre struct et class

En C with Classesstruct et class étaient très différents.

Les structures fonctionnaient comme en C, structures de données simples, alors que c’est au sein des classes que sont nés les concepts de méthodes et d’attributs.

Donc à l’époque, la différence entre les deux était bien réelle, d’où l’utilité d’une telle distinction6.

Vers le C++

Les deux plus grandes features du début du C++ étaient les fonctions virtuelles et la surcharge de fonction.

En plus de ça, des règles pour les espaces de nom ont été introduits en C++. Parmi elles :

  • Les noms (au sein d’un périmètre) sont privés sauf s’ils sont explicitement déclarés publics.
  • Une classe est un périmètre (impliquant que les périmètres de classe s’emboîtent correctement).
  • Les structures C ne s’emboîtent pas (même si elles s’emboîtent lexicalement).

Ces règles font que les structures et les classes se comportent différemment en terme de périmètre et de nom.

Par exemple, ceci était légal à cette époque :

struct outer {
    struct inner {
        int i;
    };
};
 
struct inner a = { 1 };

Mais si vous changiez les struct en class, vous aviez une erreur de compilation (il eût fallu écrire outer::inner au lieu de struct inner).

« Fusion » avec le mot-clé struct

Il est assez difficile de donner une date précise pour le moment où les deux mots-clés ont « fusionné » pour devenir plus ou moins équivalents, parce qu’il n’y a aucune source qui le dit explicitement. Mais nous pouvons mener l’enquête.

D’après The C++ Programming Language – Reference Manual (Bjarne Stroustrup, 1984), la première version publiée du langage C++ :

Les classes contiennent une séquence d’objets de types variés, un ensemble de fonctions pour manipuler ces objets et un ensemble de restriction sur l’accès à ces objets et fonctions.
Les structures sont des classes sans restriction d’accès.
—Bjarne Stroustrup, The C++ Promgramming Language – Reference Manual, §4.4 Derived Types

De plus, si on regarde la retrospective de Stroustrup donne sur les fonctions virtuelles et le modèle d’agencement des objets (concepts introduits en 1986) :

À ce moment, le modèle d’objet devient réel dans le sens qu’un objet est bien plus qu’une simple agrégation de données […] Alors pourquoi n’ai-je pas, à ce moment, choisi de faire en sorte que les structures et les classes soient des notions différentes ?
Mon intention était d’avoir un concept unique : un unique ensemble de règles d’agencement, un unique ensemble de règles de correspondance, un unique ensemble de règles de résolution, etc. […]
—Bjarne Stroustrup, The Design and Evolution of C++, §3.5.1 The Object Layout Model

Même s’il semble que les structures ne pouvaient pas avoir de membres privés à l’époque (le mot-clé private n’existait pas encore !), on peut confortablement avancer que c’est à cette époque que les struct et les class ont été « fusionnés ».

Mais quand est-ce qu’ils sont réellement devenus identiques

Techniquement, les structures ne pouvaient toujours pas émuler des classes au moment de la création du C++. Il faut donc chercher l’invention du mot-clé private pour avoir cette correspondance.

Il semblerait que ce soit arrivé en même temps que la création du mot-clé protected, qui a été introduit en 1987, pour la version 1.2 du langage.

Depuis lors, jusqu’à maintenant

Malgré tout ce qu’on a pu voir à ce sujet, et le fait qu’aujourd’hui, class est techniquement inutile, il y a plus à cela que les considérations techniques.

Car le mot-clé class a acquis de la sémantique.

En effet, écrire le mot class sert à indiquer qu’on est en train d’implémenter une classe qui n’est pas juste un sac de données, tandis que le mot-clé est principalement réservé à cet usage. L’usage des mots-clés diffèrent de leur technicité. Au cours de leur histoire, ils ont chacun acquis un sens.

L’article de Jonathan Boccara sur le sujet est très pertinent : The real difference between struct and class – Fluent C++ (fluentcpp.com). Cet article s’inspire des Core Guidelines du C++.

Le fait que class a un vécu de plus de quarante ans le rend très différent que le class de 1980 et du class qu’on aurait hypothétiquement introduit dans le C++20 en partant de rien.

Mais la question qui me vient à l’esprit est la suivante : est-ce qu’on doit continuer d’utiliser class ainsi ? Est-ce qu’on devrait maintenir la sémantique acquise ou devrait-on chercher à faire évoluer son sens vers plus de modernité ?

La réponse est plutôt simple : ça dépend de chacun de nous. Nous, développeurs C++, sommes ceux qui faisons évoluer le langage, chaque jour, pour chaque ligne qu’on écrit.

Les Core Guidelines nous disent comment on devrait utiliser chaque fonctionnalité du C++ aujourd’hui, mais peut-être que demain, quelqu’un (vous ?) trouvera une meilleure manière de coder, plus claire et plus sécurisée. Comprendre ce que sont les structures et les classes dans le présent et ce qu’elles ont été dans le passé est le premier pas pour définir ce qu’elle seront demain.

En conclusion

La meilleure façon de résumer cet article est la suivante : « Les structures du C et les classes du Simula ont fusionné à la création du C++ », mais on peut aussi que, grâce à cela, malgré le fait qu’elles représentent la même fonctionnalité, elles ont un sens différent.

Cet article n’est pas un pamphlet contre class et je ne conclurai pas cet article par un argument mi-éclairé mi-autoritaire comme j’ai l’habitude de faire7. À la place, je vous dirai simplement que j’ai réalise à quel il était important de toujours contextualiser les articles comme celui-ci avec la version de C++ dans laquelle ils ont été pensés.

Je pense qu’il est important de comprendre l’histoire et d’être capable de juger les pratiques qu’on emploie aujourd’hui. Est-ce qu’on fait telle ou telle chose par habitude, où y a-t-il de réels avantages à cela ? On doit se poser cette question tous les jours, à défaut de quoi on finira fatalement par écrire du code obsolète, dans un mode de pensée obsolète.

La manière dont les développeurs C++ pensent8 évolue de décennie en décennie. À chaque ère, les développeurs ont un état d’esprit différent, des buts différents, des problématiques différentes, une éducation différentes, etc. Je ne reproche pas leur manière de coder au développeurs du passé, mais je blâmerai toujours les gens d’aujourd’hui pour ce qu’ils codent avec des us dépassés. Et dans le futur, j’espère que mes pairs sauront me pointer du doigt quand je m’abaisserai moi-même à écrire du « vieux C++ ».

Merci de votre attention et à la semaine prochaine !

Article original : [History of C++] Explanation on why the keyword `class` has no more reason to exist | Belay the C++ (belaycpp.com)
Traductrice : Chloé Lourseyre

Addenda

Pour aller un peu plus loin

Dans cette sous-section se trouvent deux petites idées qui sont un peu hors-sujet.

Les espaces de nom en C

Quelque chose qui n’a pas été hérité du C en C++ est l’espace de nom des structures.

En C, l’espace qui contient les noms de structure n’est pas le même que l’espace global. En C, struct foo et foo ne font pas référence au même objet. Ce n’est pas le cas en C++. Pour peu que foo soit une structure, struct foo et foo sont le même nom.

Il y a une manière, en C, de faire le lien entre les deux espaces de nom. Il suffit pour ça d’utiliser typedef. Pour plus d’information, suivez le lien suivant : How to use the typedef struct in C (educative.io).

Le mot-clé class dans les templates

Peut-être avez-vous déjà vu cette syntaxe :

template <class C_>
void foo(C_ arg)
{
    // ...
}

Que veut dire le mot-clé class dans ce contexte ?

Cela ne veut rien de plus que « un type, n’importe lequel ».

C’est un peu maladroit, puisque l’utilisation du mot-clé peut laisser penser que C_ doit être une classe, alors que ça peut très bien être un type primitif. Plus tard, le mot-clé typename a été introduit pour un peu plus de genéricité, mais le mot-cle class peut encore être utilisé

Annotations

1. Comme `struct` et `class` sont si similaire, je vais considérer arbitrairement que `class` est le mot-clé « en trop », tout simplement parce que `struct` existait déjà en C.

2. Cet argument est incorrect. Par définition, le sucre syntaxique est censé rendre le code plus simple à lire ou écrire. Comme struct/class n’est qu’une permutation de mot-clé, il n’y a pas de gain de clarté quel qu’il soit. La seule raison pour laquelle le code peut sembler plus simple à lire en les utilisant est lié à leur sémantique, pas à la syntaxe.

3. Oui, je sais, il existe des développeurs qui utilisent les modifieurs d’accès implicites. J’en fais moi-même partie. Mais ce qu’on a tendance à oublier avec ce genre d’argument c’est que la majorité de l’industrie du logiciel ne code pas comme nous (et cette phrase est vraie pour la plupart des développeurs). Les faits que je soutiens ici sont empiriques. Une pratique individuelle, aussi saine soit-elle, ne peut pas être un argument contre l’établissement de ce fait.

4. C’est techniquement vrai, mais le raisonnement est à l’envers. C’est parce que leur duplicité est historique qu’ils ont acquis de la sémantique, par l’inverse. Si ces deux mots-clés étaient créés aujourd’hui, à partir de rien, ils n’auraient pas spécialement de sémantique et sembleraient redondant pour la majorité des gens. Je traite ce sujet plus en profondeur vers la fin de l’article.

5. Le nom « Simula » désigne deux vieux langages de programmation, le Simula I et le Simula 67, développés dans les années soixante au *Norvegian Computing Center* à Oslo. Il est considéré comme le premier langage de programmation orienté-objet. Bien qu’il soit très méconnu au sein de la communauté de développeurs, il est l’influence de beaucoup de langages de programmation modernes, certains largement utilisés aujourd’hui, comme l’Objective Pascal, le Java, le C# et, bien entendu, le C++.

6. À cette époque, on pouvait émuler n’importe quelle structure avec une classe, mais il était quand même intéressant, en particulier avec l’état d’esprit de l’époque, de faire la distinction.

7. J’ai tendance à toujours être d’accord avec les [C++ Core Guidelines (isocpp.github.io)][9], même si j’essaye toujours de garder un esprit critique. Mais gardez en tête que les *guidelines* d’aujourd’hui ne sont pas forcément celles de demain.

8. Je pense que cette assertion est vraie pour tous les langages, mais est particulièrement flagrante pour le C++, puisqu’il s’agit d’un des plus vieux langages parmi les plus populaires sur le marché, aujourd’hui en 2021.

Sources

Par ordre d’apparition :

Laisser un commentaire