Demat'iNal,
Un collègue a récemment piqué mon intérêt (ouille) avec ce petit bout de code :
#include <map>
#include <iostream>
int main() {
std::map<int, int> m;
m[1] = m.size();
std::cout << m[1] << std::endl;
return 0;
}
Qui, compilé par deux version différentes de GCC (9.2 et 5.1) donne deux résultats différents (voir https://godbolt.org/z/4_xVPV).
Qu'en déduire ? Bug dans le compilateur ? Comportement indéfini ? Autre chose ? Des fragments de réponse se cachent dans le titre…
# Comportement indéfini
Posté par Clément V . Évalué à 8.
L'ordre entre
m[1]
etm.size()
n'est pas déterminé.operator=
est exécuté en dernier mais ce n'est pas lui qui ajoute l'élément, c'estm[1]
.[^] # Re: Comportement indéfini
Posté par Patrick Nicolas . Évalué à 6.
Indéfini avant C++17.
À partir de C++17 c'est défini d'après https://en.cppreference.com/w/cpp/language/eval_order
Règle 16) Every overloaded operator obeys the sequencing rules of the built-in operator it overloads when called using operator notation
Et 20) In every simple assignment expression E1=E2 and every compound assignment expression E1@=E2, every value computation and side-effect of E2 is sequenced before every value computation and side effect of E1
m.size()
est évalué en premier, puism[1]
. Le bon résultat est celui retourné par gcc 9.[^] # Re: Comportement indéfini
Posté par David Marec . Évalué à 6.
Il y a même eu une dépêche publiée sur le sujet.
[^] # Re: Comportement indéfini
Posté par Kerro . Évalué à 5.
GCC 5.1 : publié en 2015
GCC 9.2 : publié en 2019
C++17 a été publié entre les deux, du coup pas anormal que le résultat diffère.
[^] # Re: Comportement indéfini
Posté par Clément V . Évalué à 1.
Je ne savais que la règle avait changé. C'est quand même étrange de voir = comme un point de séquence.
Mais là
-std=c++11
est passé au deux compilateurs, donc on est bien dans un cas où l'ordre est indéfini, non ?[^] # Re: Comportement indéfini
Posté par EdB . Évalué à 3.
Si la version pre 2017 est indéfinie, ça veut dire qu'un nouveau compilateur peut très bien adopter le comportement 2017, même si le switch de version est inférieur.
C'est qui est plus logique niveau implémentation algorithmique.
[^] # Re: Comportement indéfini
Posté par David Marec . Évalué à 3.
… Ce qui signifie que le compilateur fait ce qu'il veut.
# Sequence point
Posté par Pierrick Bouvier . Évalué à 4. Dernière modification le 21 février 2020 à 15:05.
Ce que tu demandes n'est pas lié à la surcharge d'opérateur.
Le standard précise que tous les effets de bord d'un statement doivent être terminés au point de séquence (ici, le ;). Mais sans préciser l'ordre d'évaluation. Notamment, = n'est pas un point de séquence.
La ligne
m[1] = m.size();
Peut donc soit d'abord évaluer m[1], soit m.size(), avant d'effectuer l'affectation.
Un exemple classique est i = i++;
https://en.wikipedia.org/wiki/Sequence_point
# Undefined != Unspecified
Posté par serge_sans_paille (site web personnel) . Évalué à 2.
Ce comportement est plutôt non-spécifié, ce qui est (heureusement) différent d'indéfini !
D'après un draft du standard pris au hasard, dans la section
3 Terms and definitions[intro.defs]
On notera d'ailleurs l'élégant
[^] # Re: Undefined != Unspecified
Posté par Matthieu Moy (site web personnel) . Évalué à 2.
On est d'accord sur les définitions, mais justement pour l'ordre d'évaluation le standard parle bien de undefined behavior. Sur la version du standard que tu donnes, ce n'est plus le cas car justement ça a été changé en 2017, mais si je prends par exemple ce draft de 2014 page 10,
i = i++ + 1
est clairement marqué comme un UB (et oui, c'est flippant).# C++82
Posté par dnanar . Évalué à -6. Dernière modification le 24 février 2020 à 01:28.
Raisons de plus pour:
1. utiliser la surcharge d’opérateur quand y a vraiment aucun autre moyen de rendre le code plus clair
2. passer a Rust :P (qui n'est pas défini par un standard ISO-ique, je vous l'accorde)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.