Edit: Merci pour les commentaires par Deduplicator et user694733, ici est une version modifiée de ma réponse originale à cette question.
La version C++ a pas défininon spécifiée comportement.
Il y a une subtile différence entre "undefined" et "non spécifié", en ce que la première permet à un programme de faire quelque chose (y compris de s'écraser) tandis que le second permet de choisir à partir d'un ensemble de notamment permis à des comportements sans dicter le choix est correct.
À l'exception de très rares cas, vous aurez toujours envie d'éviter à la fois.
Un bon point de départ pour comprendre l'ensemble de la question sont le C++ Faq Pourquoi certaines personnes pensent que x = ++o + o++ est mauvais? , Quelle est la valeur de i++ + ++ i? et Quel est le problème avec "séquence de points"?:
Entre le précédent et suivant de la séquence de point un scalaire objet est
avoir son stockée la valeur modifiée au plus une fois par l'évaluation d'une
de l'expression.
(...)
Pour l'essentiel, dans le C et le C++, si vous lisez une variable deux fois dans une expression
où tu écris aussi, le résultat est indéfini.
(...)
À certains points spécifiques de la séquence d'exécution appelée séquence
points, tous les effets secondaires des évaluations précédentes sont complètes et
pas d'effets secondaires de la suite des évaluations ont eu lieu. (...)
La "certains points" que l'on appelle séquence de points sont(...)
après évaluation de l'ensemble de la fonction paramètres , mais avant la première
expression au sein de la fonction est exécutée.
En bref, la modification d'une variable à deux reprises entre deux années consécutives de la séquence de points de rendements comportement indéfini, mais un appel de fonction introduit un intermédiaire point de séquence (en fait, deux intermédiaires séquence de points, parce que l'instruction de retour crée un autre).
Cela signifie que le fait que vous avez un appel de fonction dans votre expression "sauve" votre Simple::X += Simple::f();
ligne de est indéfini et il se transforme en "seulement" non spécifié.
1 et 11 sont possibles et de corriger les résultats, alors que l'impression 123, s'écraser ou de l'envoi de l'insulter e-mail à votre patron ne sont pas autorisés comportements; vous aurez jamais obtenir une garantie de savoir si 1 ou 11 sera imprimé.
L'exemple suivant est légèrement différente. C'est apparemment une simplification du code original mais vraiment sert à mettre en évidence la différence entre l'indéfini et indéterminé comportement:
#include <iostream>
int main() {
int x = 0;
x += (x += 10, 1);
std::cout << x << "\n";
}
Ici, le comportement est en effet pas défini, car l'appel de la fonction a disparu, de sorte que les deux modifications de l' x
se produisent entre deux années consécutives de la séquence de points. Le compilateur est permis par le langage C++ cahier des charges pour créer un programme qui imprime 123, les accidents ou envoie une insulte e-mail à votre patron.
(L'e-mail de chose est bien sûr juste une très fréquent d'humour tenter d'expliquer comment indéfini signifie vraiment quelque chose se passe. Les pannes sont souvent un résultat plus réaliste du comportement indéfini.)
En fait, l' , 1
(tout comme l'instruction de retour dans votre code original) est un leurre. La suite de rendements comportement indéfini, trop:
#include <iostream>
int main() {
int x = 0;
x += (x += 10);
std::cout << x << "\n";
}
Cela peut imprimer 20 (il le fait sur ma machine avec VC++ 2013), mais le comportement n'est toujours pas défini.
(Remarque: cela s'applique à des opérateurs. La surcharge d'opérateur modifie le comportement spécifié, parce que les opérateurs surchargés copie de la syntaxe à partir de l'intégré, mais ont la sémantique des fonctions, ce qui signifie que surchargé +=
opérateur de type personnalisé apparaît dans une expression est en fait un appel de fonction. Par conséquent, non seulement la séquence de points mis en place, mais l'ensemble de l'ambiguïté disparaît, l'expression de devenir l'équivalent d' x.operator+=(x.operator+=(10));
, ce qui a garanti l'ordre de l'argument de l'évaluation. Ce n'est probablement pas pertinent à votre question, mais doit être mentionné de toute façon.)
En revanche, la version de Java
import java.io.*;
class Ideone
{
public static void main(String[] args)
{
int x = 0;
x += (x += 10);
System.out.println(x);
}
}
doit imprimer 10. C'est parce que Java n'a ni indéfini, ni quelconque comportement en ce qui concerne l'ordre d'évaluation. Il n'y a pas de séquence de points d'inquiétude. Voir Java Language Specification 15.7. Ordre D'Évaluation:
Le langage de programmation Java garantit que les opérandes de
les opérateurs semblent être évalué dans un ordre d'évaluation,
à savoir, de gauche à droite.
Ainsi, dans le Java cas, x += (x += 10)
, interprété à partir de la gauche vers la droite, signifie que la première chose est ajouté à 0, et que quelque chose est 0 + 10. Donc 0 + (0 + 10) = 10.
Voir aussi par exemple 15.7.1-2 dans la spécification de Java.
Pour en revenir à votre exemple d'origine, ce qui signifie aussi que l'exemple plus complexe avec la variable statique est définie et précisée comportement en Java.
Honnêtement, je ne sais pas à propos de C# et PHP mais je pense que les deux d'entre eux ont une garantie de l'évaluation et de commande ainsi. C++, contrairement à la plupart des autres langages de programmation (mais comme C) tend à permettre beaucoup plus d'indéfini et indéterminé de comportement que les autres langues. Ce n'est pas de bon ou de mauvais. C'est un compromis entre la robustesse et l'efficacité. Choisir le bon langage de programmation pour une tâche particulière ou d'un projet est toujours une question d'analyser les compromis.
Dans tous les cas, les expressions avec ces effets secondaires sont mauvais style de programmation dans les quatre langues.
Un dernier mot:
J'ai trouvé un petit bug dans le C# et Java.
Vous ne devriez pas supposer pour trouver des bogues dans les spécifications de langue ou de compilateurs si vous n'avez pas beaucoup d'années d'expérience professionnelle en tant qu'ingénieur logiciel.