Je suis intéressé par l'idée de C ++ - comme const
pas cette exécution particulière (comme le rejet const
).
Prenez par exemple C # - il manque de const comme C ++, et la raison en est l'habituel - les gens et le temps. Ici aussi, il semble que l'équipe C # ait examiné l'exécution C ++ de const
CLR marketing et en avait assez à ce stade (voir pourquoi il n'y a pas de méthode membre const dans c # et paramètre const ; merci Svick). Si nous allons plus loin, est-ce autre chose?
Y a-t-il quelque chose de plus profond? Prenons par exemple le multi-héritage - il est généralement considéré comme difficile à saisir (pour l'utilisateur) et donc non ajouté à la langue (comme le problème du diamant).
Y at - il quelque chose dans NATURE de const
cette pose un problème qu'il est préférable de la langue pour l' éviter? Quelque chose comme décider si elle const
doit être profonde ou peu profonde (avoir un const
conteneur signifie que je ne peux pas ajouter de nouveaux éléments ou modifier les éléments existants; que faire si les éléments sont de type référence)?
MISE À JOUR : alors que je mentionne C #, et faisant ainsi une perspective historique, je m'intéresse à la nature des problèmes potentiels const
du langage.
Ce n'est pas un défi des langues. Veuillez ignorer des facteurs tels que les tendances actuelles ou la popularité - je ne m'intéresse qu'aux problèmes techniques - merci.
la source
Réponses:
Notez bien que cela vous convient, mais dans les langages fonctionnels comme Standard ML, tout est immuable par défaut. La mutation est prise en charge via un
ref
type d'érence générique . Ainsi, uneint
variable est immuable et uneref int
variable est un conteneur mutable pourint
s. Fondamentalement, les variables sont de vraies variables au sens mathématique (une valeur inconnue mais fixe) etref
s sont des "variables" au sens impératif de la programmation - une cellule mémoire qui peut être écrite et lue. (J'aime les appeler assignables .)Je pense que le problème
const
est double. Tout d'abord, C ++ manque de garbage collection, ce qui est nécessaire pour avoir des structures de données persistantes non triviales .const
doit être profond pour avoir un sens, mais avoir des valeurs totalement immuables en C ++ n'est pas pratique.Deuxièmement, en C ++, vous devez vous inscrire
const
plutôt que de vous en retirer. Mais lorsque vous oubliezconst
quelque chose et que vous le corrigez plus tard, vous vous retrouverez dans la situation «empoisonnement const» mentionnée dans la réponse de @ RobY où leconst
changement se répercutera en cascade dans le code. Siconst
c'était la valeur par défaut, vous ne vous retrouveriez pas à appliquerconst
rétroactivement. De plus, devoir ajouterconst
partout ajoute beaucoup de bruit au code.Je soupçonne que les langages traditionnels qui ont suivi (par exemple Java) ont été fortement influencés par le succès et la façon de penser de C et C ++. Exemple: même avec la récupération de place, la plupart des API de collecte de langues supposent des structures de données mutables. Le fait que tout soit mutable et immuabilité est considéré comme un cas d'angle en dit long sur l'état d'esprit impératif derrière les langues populaires.
EDIT : Après réflexion sur commentaire de greenoldman, je me suis rendu compte qu'il
const
ne s'agissait pas directement de l'immuabilité des données;const
encode dans le type de la méthode si elle a des effets secondaires sur l'instance.Il est possible d'utiliser la mutation pour obtenir un comportement référentiellement transparent . Supposons que vous ayez une fonction qui, lorsqu'elle est appelée, renvoie successivement différentes valeurs - par exemple, une fonction qui lit un seul caractère
stdin
. Nous pourrions utiliser le cache / mémoriser les résultats de cette fonction pour produire un flux de valeurs référentiellement transparent. Le flux serait une liste chaînée dont les nœuds appellent la fonction la première fois que vous essayez de récupérer leur valeur, mais mettent ensuite en cache le résultat. Donc, sistdin
constainsHello, world!
, la première fois que vous essayez de récupérer la valeur du premier nœud, il en lira unchar
et reviendraH
. Ensuite, il continuera à revenirH
sans autres appels pour lire achar
. De même, le second noeud lisait unchar
destdin
la première fois que vous essayez de récupérer sa valeur, cette fois-ci en retournante
et en mettant en cache ce résultat.La chose intéressante ici est que vous avez transformé un processus intrinsèquement dynamique en un objet apparemment apatride. Cependant, il était nécessaire de muter l'état interne de l'objet (en mettant en cache les résultats) pour y parvenir - la mutation était un effet bénin . Il est impossible de faire notre
CharStream
const
même si le flux se comporte comme une valeur immuable. Imaginez maintenant qu'il existe uneStream
interface avec desconst
méthodes, et que toutes vos fonctions s'y attendentconst Streams
. VotreCharStream
ne peut pas implémenter l'interface!( EDIT 2: Apparemment, il y a un mot-clé C ++ appelé
mutable
qui nous permettrait de tricher et de faireCharStream
const
Cependant, cette lacune Détruit.const
Les garanties s de - maintenant vous ne pouvez vraiment pas être sûr que quelque chose ne va pas muter par sesconst
méthodes que je suppose que ce n'est pas. mauvais car vous devez explicitement demander l'échappatoire, mais vous êtes toujours complètement dépendant du système d'honneur.)Deuxièmement, supposons que vous ayez des fonctions d'ordre élevé, c'est-à-dire que vous pouvez passer des fonctions comme arguments à d'autres fonctions.
const
ness fait partie de la signature d'une fonction, vous ne pourrez donc pas passer des non-const
fonctions comme arguments aux fonctions qui attendentconst
fonctions. Une application aveugleconst
entraînerait une perte de généralité.Enfin, la manipulation d'un
const
objet ne garantit pas qu'il ne mute pas un état externe (statique ou global) derrière votre dos, doncconst
les garanties ne sont pas aussi fortes qu'elles le paraissent initialement.Il n'est pas clair pour moi qu'encoder la présence ou l'absence d'effets secondaires dans le système de type est universellement une bonne chose.
la source
var
à la demande ne laisserait qu'un seul problème - la profondeur?const
référence à un non-const
objet. Même ainsi, je ne pense pas qu'il soit logique d'avoir un peu profondconst
. Tout l'intérêtconst
est de garantir que vous ne pourrez pas modifier l'objet via cette référence. Vous n'êtes pas autorisé à contournerconst
en créant une non-const
référence, alors pourquoi serait-il acceptable de contournerconst
en mutant les membres de l'objet?func foo(const String &s)
et c'est un signals
qui ne sera pas modifiéfoo
.null
héritage.)Le problème principal est que les programmeurs ont tendance à ne pas l'utiliser suffisamment, donc quand ils atteignent un endroit où cela est nécessaire, ou qu'un responsable souhaite plus tard corriger la const-correct, il y a un énorme effet d'entraînement. Vous pouvez toujours écrire du code immuable parfaitement bon sans
const
, votre compilateur ne le fera pas appliquer et aura plus de mal à l'optimiser. Certaines personnes préfèrent que leur compilateur ne les aide pas. Je ne comprends pas ces gens, mais il y en a beaucoup.Dans les langages fonctionnels, à peu près tout est
const
, et être mutable est la rare exception, si elle est autorisée. Cela présente plusieurs avantages, tels qu'une concurrence plus facile et un raisonnement plus facile sur l'état partagé, mais cela prend un certain temps pour s'y habituer si vous venez d'une langue avec une culture mutable. En d'autres termes, ne pas vouloirconst
est une question de personnes, pas une question technique.la source
Je vois les inconvénients comme:
"empoisonnement const" est un problème lorsqu'une déclaration de const peut forcer une déclaration précédente ou suivante à utiliser const, et cela peut faire en sorte que ses déclarations précédentes ou suivantes utilisent const, etc. Et si l'empoisonnement const circule dans une classe d'une bibliothèque que vous n'avez pas de contrôle, alors vous pouvez rester coincé dans un mauvais endroit.
ce n'est pas une garantie absolue. Il y a généralement des cas où le const protège uniquement la référence, mais est incapable de protéger les valeurs sous-jacentes pour une raison ou une autre. Même en C, il y a des cas limites auxquels const ne peut pas accéder, ce qui affaiblit la promesse que const fournit.
en général, le sentiment de l'industrie semble s'éloigner des garanties de compilation solides, quel que soit le réconfort qu'elles peuvent vous apporter, moi et moi. guy a sorti 10 modules Python parfaitement fonctionnels, bien conçus, bien testés et entièrement fonctionnels. Et il ne piratait même pas quand il l'a fait.
Alors peut-être que la question est de savoir pourquoi poursuivre une fonctionnalité potentiellement controversée sur laquelle même certains experts sont ambivalents lorsque vous essayez de faire adopter votre nouveau langage.
EDIT: réponse courte, une nouvelle langue a une pente raide à gravir pour adoption. Ses concepteurs doivent vraiment réfléchir aux fonctionnalités dont il a besoin pour être adoptés. Prenez Go, par exemple. Google a en quelque sorte brutalement tronqué certaines des batailles religieuses les plus profondes en faisant des choix de conception de style Conan-le-Barbare, et je pense en fait que le langage est meilleur pour cela, d'après ce que j'ai vu.
la source
int *
pointeur mutable sur valeurint const *
mutable, pointeur mutable sur valeurint * const
const, pointeur const sur valeur mutable,int const * const
pointeur const sur valeur const.const
), donc de telles "raisons" que je considère non pertinentes (pour cette question au moins). À propos de l'const
empoisonnement - pouvez-vous donner un exemple? Parce qu'AFAIK c'est l'avantage, tu passes quelque choseconst
et ça doit resterconst
.const
problème mais vraiment changer le type - quoi que vous fassiez, ce sera toujours un problème. Pensez à passerRichString
, puis réalisez que vous feriez mieux de passerString
.const
. Il est trop facile de ne pas faireconst
ce qui devrait êtreconst
parce que vous devez y adhérer au lieu d'en sortir.Il y a quelques problèmes philosophiques à ajouter
const
à une langue. Si vous déclarezconst
à une classe, cela signifie que la classe ne peut pas changer. Mais une classe est un ensemble de variables couplées à des méthodes (ou mutateurs). Nous obtenons l'abstraction en cachant l'état d'une classe derrière un ensemble de méthodes. Si une classe est conçue pour cacher l'état, alors vous avez besoin de mutateurs pour changer cet état de l'extérieur et ce n'estconst
plus le cas. Il n'est donc possible de déclarerconst
que des classes immuables. Donc ,const
semble possible que dans des scénarios où les classes (ou les types que vous voulez déclarerconst
) sont insignifiants ...Alors avons-nous besoin de
const
toute façon? Je ne pense pas. Je pense que vous pouvez facilement vous en passerconst
si vous avez un bon design. De quelles données avez-vous besoin et où, quelles méthodes sont des méthodes de données à données et quelles abstractions avez-vous besoin pour les E / S, le réseau, la vue, etc. Ensuite, vous divisez naturellement les modules et les classes qui traitent de l'état et vous avez des méthodes et classes immuables qui n'ont pas besoin d'état.Je pense que la plupart des problèmes surviennent avec de gros objets de données volumineux qui peuvent se sauvegarder, se transformer et se dessiner et ensuite vous voulez le passer à un moteur de rendu par exemple. Vous ne pouvez donc pas copier le contenu des données, car c'est trop cher pour un gros objet de données. Mais vous ne voulez pas non plus que cela change, vous avez donc besoin de quelque chose comme
const
vous le pensez. Laissez le compilateur découvrir vos défauts de conception. Cependant, si vous divisez ces objets en un seul objet de données immuable et implémentez les méthodes dans le module responsable, vous pouvez passer (uniquement) le pointeur et faire les choses que vous voulez faire avec les données tout en garantissant l'immuabilité.Je sais qu'il est possible en C ++ de déclarer certaines méthodes
const
et de ne pas déclarer sur d'autres méthodes, car ce sont des mutateurs. Et puis quelque temps plus tard, vous avez besoin d'un cache, puis un getter mute un objet (car il se charge paresseusement) et ce n'estconst
plus le cas. Mais c'était tout l'intérêt de cette abstraction, n'est-ce pas? Vous ne savez pas quel état est muté à chaque appel.la source
const
(dans le sens C ++ ) vous devez définir l'interface, également pour toutes les propriétés.