Mise à JOUR: Cette question a été l'objet de mon blog, le 18 juin 2012. Merci pour la grande question!
Pourquoi? Je veux savoir si c'était une décision de conception et, dans l'affirmative, ce qui a incité il?
Vous êtes à la demande, en substance, pour le procès-verbal de la réunion du C ANSI comité de conception, et je n'ai pas de ceux à portée de main. Si votre question ne peut être répondu définitivement par quelqu'un qui était dans la chambre, alors vous allez avoir à trouver quelqu'un qui était dans la salle.
Cependant, je peux répondre à une question plus large:
Quels sont certains des facteurs qui mènent une langue comité de conception de quitter le comportement d'un programme juridique (*
) "undefined" ou "de mise en œuvre définies" (**
)?
Le premier facteur majeur est: il y a deux implémentations existantes de la langue dans le marché qui sont en désaccord sur le comportement d'un programme particulier? Si FooCorp du compilateur compile M(A(), B())
"call A, B, appel M", et BarCorp du compilateur compile comme "appel en B, Un appel, appel M", et ni est la "évidemment correct" comportement puis il y a une forte incitation à la langue de conception de dire "vous êtes tous les deux de droite", et en faire la mise en œuvre de comportement défini. En particulier c'est le cas si FooCorp et BarCorp les deux ont des représentants sur le comité.
L'autre facteur important est: la fonctionnalité naturellement présente beaucoup de possibilités de mise en œuvre? Par exemple, en C# le compilateur de l'analyse d'une requête "compréhension" de l'expression est spécifié comme "ne syntaxique, la transformation en un programme équivalent qui n'ont pas d'interprétations de la requête, puis de les analyser qui est normalement le programme". Il y a très peu de liberté pour une mise en œuvre afin de faire autrement.
En revanche, le C# spécification indique que l' foreach
boucle doivent être considérées comme équivalentes while
boucle à l'intérieur d'un try
bloc, mais permet la mise en œuvre d'une certaine souplesse. Un compilateur C# est permis de dire, par exemple "je sais comment le mettre en oeuvre foreach
boucle de la sémantique de manière plus efficace sur un tableau" et utiliser le tableau d'indexation de l'entité plutôt que de la conversion de la matrice à une séquence que la spécification suggère qu'il devrait.
Un troisième facteur est: est la fonction si complexe qu'une ventilation détaillée de son comportement serait-il difficile ou cher de le préciser? La spécification C# est dit très peu d'effet sur la façon dont les méthodes anonymes, des expressions lambda, des arbres d'expression, dynamique des appels, iterator blocs et async blocs sont mises en œuvre; il se contente de décrire le désiré de la sémantique et de certaines restrictions sur le comportement, et laisse le reste jusqu'à la mise en œuvre.
Un quatrième facteur: la fonctionnalité d'imposer un lourd fardeau sur le compilateur à analyser? Par exemple, en C# si vous avez:
Func<int, int> f1 = (int x)=>x + 1;
Func<int, int> f2 = (int x)=>x + 1;
bool b = object.ReferenceEquals(f1, f2);
Supposons que nous avons besoin de b pour être vrai. Comment allez-vous déterminer si deux fonctions sont "les mêmes"? Faire un "intensionality" analyse -- les corps de fonction ont le même contenu? -- est dur, et de faire une "fusion" analyse -- les fonctions ont les mêmes résultats pour les mêmes entrées? -- est encore plus difficile. Un langage de spécification du comité devrait chercher à minimiser le nombre de problèmes de recherche que l'équipe de mise en oeuvre pour le résoudre!
En C# c'est donc parti pour être mise en œuvre définis; un compilateur peut choisir d'en faire référence égale ou non à sa discrétion.
Un cinquième facteur: la fonctionnalité d'imposer un lourd fardeau sur l'environnement d'exécution?
Par exemple, en C# déréférencement passé la fin d'un tableau est bien définie; elle produit un tableau d'index est en dehors des limites de l'exception. Cette fonction peut être mis en œuvre avec une petite -- pas de zéro, mais les petits-coût à l'exécution. L'appel d'une instance ou d'une méthode virtuelle avec une valeur null récepteur est définie comme la production d'un null-a-été déréférencé exception; encore une fois, cela peut être mis en œuvre avec une petite mais non nulle coût. L'avantage d'éliminer les comportements indéfinis paie pour les petites exécution de coût.
Un sixième facteur est: ne faire que le comportement défini exclure certains grands optimisation? Par exemple, C# définit l'ordre des effets secondaires lorsqu'on l'observe à partir du thread qui provoque des effets secondaires. Mais le comportement d'un programme qui observe les effets secondaires d'un thread à partir d'un autre thread est mise en œuvre définies à l'exception de quelques "spécial" des effets secondaires. (Comme une écriture volatile, ou en entrant une serrure.) Si le langage C# a exigé que tous les threads d'observer les mêmes effets secondaires dans le même ordre, alors nous aurions à restreindre les processeurs modernes de faire leur travail efficacement; les processeurs modernes dépendent de l'exécution et sophistiqués, les stratégies de mise en cache afin d'obtenir leur haut niveau de performance.
Ce ne sont que quelques facteurs qui viennent à l'esprit; il y a, bien sûr, beaucoup, beaucoup d'autres facteurs que la langue de la conception des comités de débat avant de prendre une fonction de "mise en œuvre définies" ou "indéfini".
Maintenant, nous allons revenir à votre exemple.
Le langage C# ne faire qu'un comportement strictement définies(†
); l'effet secondaire de l'augmentation est observée pour arriver avant les effets secondaires de la cession. Donc, il ne peut y avoir aucune "eh bien, c'est juste impossible" argument, parce qu'il est possible de choisir un comportement et de s'y tenir. Ni ce que cela empêche de grandes possibilités d'optimisations. Et il n'y a pas une multiplicité de possibles complexe la mise en œuvre des stratégies.
Ma supposition, donc, et j'insiste sur le fait que c'est une supposition, c'est que le langage C comité de commande sont les effets secondaires de la mise en œuvre du comportement défini parce qu'il y avait plusieurs compilateurs dans le marché qui fait les choses différemment, aucun n'a été clairement "plus correct", et le comité n'était pas disposé à dire la moitié d'entre eux qu'ils ont tort.
(*
) Ou, parfois, son compilateur! Mais nous allons ignorer ce facteur.
(**
) "Undefined" comportement signifie que le code peut faire quelque chose, y compris l'effacement du disque dur. Le compilateur n'est pas nécessaire pour générer le code qui a un comportement particulier, et n'est pas requise pour vous dire que c'est la génération de code avec un comportement indéterminé. "La mise en œuvre définies" comportement signifie que le compilateur auteur est donné une grande liberté dans le choix de la stratégie de mise en œuvre, mais il est nécessaire de choisir une stratégie, l'utiliser de manière cohérente, et le document de son choix.
(†
) Lorsqu'on les observe à partir d'un seul fil, bien sûr.