Ce qui a fait i = ++ i + 1; juridique en C++17?
Avant de commencer à hurler comportement indéfini, c'est explicitement répertoriés dans N4659 (C++17)
i = i++ + 1; //the value of i is incremented
Encore dans N3337 (C++11)
i = i++ + 1; //the behavior is undefined
Ce qui a changé?
De ce que j'ai pu rassembler, à partir de [N4659 de base.exec]
Sauf indication contraire, l'évaluation des opérandes des opérateurs individuels et des sous-expressions des expressions individuelles sont séquencé. [...] La valeur des calculs des opérandes d'un opérateur sont séquencée avant de la valeur de calcul du résultat de l'opérateur. Si un effet indésirable sur un emplacement de la mémoire est séquencé par rapport à un autre effet secondaire sur le même emplacement de mémoire ou une valeur de calcul à l'aide de la valeur de tout objet dans le même emplacement de mémoire, et ils ne sont pas potentiellement concurrentes, le comportement est indéfini.
Où valeur est défini à [N4659 de base.type]
Pour trivialement copiable types, la valeur de la représentation est un ensemble de bits dans la représentation des objets qui détermine un valeur, qui est un élément discret de la mise en œuvre-ensemble défini de valeurs
Sauf indication contraire, l'évaluation des opérandes des opérateurs individuels et des sous-expressions des expressions individuelles sont séquencé. [...] La valeur des calculs des opérandes d'un opérateur sont séquencée avant de la valeur de calcul du résultat de l'opérateur. Si un effet indésirable sur un scalaire objet est séquencé par rapport à un autre effet secondaire sur le même scalaire objet ou d'une valeur de calcul à l'aide de la valeur de la même scalaire objet, le comportement est indéfini.
De même, la valeur est définie à [N3337 de base.type]
Pour trivialement copiable types, la valeur de la représentation est un ensemble de bits dans la représentation des objets qui détermine un valeur, qui est un élément discret de la mise en œuvre-ensemble défini de valeurs.
Ils sont identiques à l'exception de la mention de la simultanéité qui n'a pas d'importance, et avec l'utilisation de emplacement de mémoire au lieu de scalaire objet, où
Arithmétique types, l'énumération des types, des types pointeur, pointeur de types de membres,
std::nullptr_t
, et de cv qualifiés versions de ces types sont appelés collectivement les types scalaires.
Qui n'affecte pas l'exemple.
De l'opérateur d'affectation (=) et le composé des opérateurs d'affectation à un groupe de droite à gauche. Tous exigent une modifiables lvalue comme leur opérande de gauche et de retourner une lvalue se référant à l'opérande de gauche. Le résultat en tout cas est un peu de champ si l'opérande de gauche est un peu de champ. Dans tous les cas, la cession est séquencé après la valeur de calcul de la droite et de la gauche opérandes, et avant que la valeur de calcul de l'expression d'affectation. L'opérande de droite est séquencée avant de l'opérande de gauche.
De l'opérateur d'affectation (=) et le composé des opérateurs d'affectation à un groupe de droite à gauche. Tous exigent une modifiables lvalue comme leur opérande de gauche et de retourner une lvalue se référant à l'opérande de gauche. Le résultat en tout cas est un peu de champ si l'opérande de gauche est un peu de champ. Dans tous les cas, la cession est séquencé après la valeur de calcul de la droite et de la gauche opérandes, et avant que la valeur de calcul de l'expression d'affectation.
La seule différence étant la dernière phrase d'être absent dans N3337.
La dernière phrase, cependant, ne devrait pas avoir d'importance que l'opérande de gauche i
est ni "un autre effet secondaire" ni "à l'aide de la valeur de la même scalaire objet" comme le id-expression est une lvalue.
- Vous avez identifié la raison: En C++17, l'opérande de droite est séquencée avant de l'opérande de gauche. En C++11 il n'y avait pas de séquençage. Ce qui, précisément, est à votre question?
- Voir la dernière phrase.
- Il y a deux séquencé effets secondaires, écrit:
i =
a pour effet de bord de la rédaction dei
.i++
a pour effet de bord de la rédaction dei
. - Si elles sont non, que ferait l'exemple dans la norme donc absolument mauvais que je serais très hésitant à se prétendent qu'ils sont séquencé.
- Ils sont non en C++11, séquencé en C++17. D'où les différents exemples.
- Quelqu'un aurait-il un lien pour la motivation de ce changement? Je voudrais un analyseur statique pour être en mesure de dire "vous ne voulez pas faire que" lorsqu'un code comme
i = i++ + 1;
. - c'est à partir de l'étude p0145r3.pdf: "Affiner l'Évaluation de l'Expression de l'Ordre pour Idiomatiques C++".
- Qui peu de ce que, exactement - je ne vois rien de pertinent, mais peut-être que c'est mon mauvais.
- Sur un rapide coup d'œil de ce document, l'article 5 implique qu'il ne couvre pas le cas de
++
. - numéro de l'article 2 dit que c'est contre-intuitif et même les experts ne parviennent pas à faire la bonne chose dans tous les cas. C'est à peu près tout de leur motivation.
- le document ajoute que le libellé de l'opérateur d'affectation. Il ne fonctionne pas sur unaire expressions dans tous les contextes, mais affectent la façon dont elles sont traitées dans le cadre de l'affectation que l'on a accepté la réponse explique.
- Je dirais qu'un compilateur qui ne peut pas lire que c'est buggé.
- N4700 est un plus projet de C++17.
- Donc, ce qui se passe quand vous allez "c=c++ +1;"? Est-ce la même chose que "c=c+2"?
- que serait c=c+1, depuis tha valeur produite par le c++ + 1 c+1.
- L'OP de la première ligne de code mentionne ce qui n'. Bien qu'à mon humble avis, le commentaire doit avoir dit "obtient 1, ajouté à cela" plutôt que "est incrémenté".
- Ces Questions sont les raisons de cette proposition existe: open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0431r0.htm mais bien sûr, il n'était pas défendu correctement à toutes les réunions du comité et probablement juste de mourir d'une mort lente.
Vous devez vous connecter pour publier un commentaire.
En C++11 la loi de la "cession", c'est à dire le côté-effet de la modification de la LHS, est séquencé après la valeur de calcul de l'opérande de droite. Notez que c'est relativement "faible" garantie: il produit de séquençage seulement à l'égard valeur de calcul de la RHS. Il ne dit rien sur la effets secondaires qui pourraient être présents dans le membre de droite, depuis l'apparition d'effets secondaires n'est pas une partie de valeur de calcul. Les exigences de C++11 établir aucun parent séquençage entre l'acte de cession et tous les effets secondaires de la RHS. C'est ce qui crée le potentiel pour UB.
Le seul espoir est dans ce cas aucune garantie supplémentaire faite par des opérateurs spécifiques utilisés dans les RHS. Si le membre de droite a utilisé un préfixe
++
, le séquençage des propriétés spécifiques à la forme préfixe de++
aurait sauvé la journée dans cet exemple. Mais postfix++
est une autre histoire: il ne fait pas de telles garanties. En C++11 les effets secondaires de=
et postfix++
fin de non séquencés avec la relation aux autres dans cet exemple. Et c'est UB.En C++17 une phrase est ajoutée à la spécification de l'opérateur d'affectation:
En combinaison avec ce qui précède, il fait une très forte garantie. Elle séquences tout qui se passe dans le membre de droite (y compris les éventuels effets secondaires) avant tout qui se passe dans le membre de GAUCHE. Depuis l'attribution est séquencé après de GAUCHE (et de DROITE), ce supplément de séquençage isole l'acte de cession de la part de tous les effets secondaires présents dans les RHS. Cette plus forte de séquençage est ce qui élimine le dessus UB.
(Mis à jour pour tenir compte de @John Bollinger commentaires.)
*foo() = globalVariable;
, par exemple, invoquerfoo()
, puis chercherglobalVariable
et de les stocker dans le pointeur reçu defoo()
. La capture de la valeur deglobalVariable
avant l'appel si le code n'a pas besoin de cela ajouterait le coût; si le code n'a besoin de la variable avant de l'appeler, de le copier à une variable temporaire ne devrait pas ajouter de coût par rapport avec le mandat de la sémantique.foo()
ne modifiera pasglobalVariable
. À moins qu'il puisse faire, il doit faire pour que la copie temporaire avant d'effectuer l'appel de la fonction.i++ + i
.i++ + i
(nii=i++
parce qu'un programmeur qui veut toute commande particulière peut facilement exiger un. Plus problématique est l'incapacité à bloquer le compilateur de la réorganisation de l'objet non qualifié accède à travers certaines opérations-dire au compilateur que dans une séquence commefoo(); barrier(); boz();
, le compilateur ne peut pas réorganiser toutes les opérations...foo
enboz
, ni vice-versa, même si ces opérations semble pour fonctionner sur "ordinaire" (non qualifié) des objets. Dans de nombreux cas, il n'est pas pratique d'utiliser des qualificatifs sur les objets accessibles via un code externe, qui pourrait être modifié de manière que le compilateur ne sait pas à propos de à temps que le code n'est pas en cours d'exécution. Notez que le fait de traiter avec matériel de réorganisation de la mémoire serait le programmeur de la responsabilité, mais un programmeur ne peut pas traiter avec le compilateur de réorganisation sans outils pour le contrôle.Vous avez identifié la nouvelle phrase
et vous avez correctement identifié que l'évaluation de l'opérande de gauche comme une lvalue est pas pertinent. Cependant, séquencée avant de spécifiée est transitive de la relation. Le complet opérande de droite (y compris la post-incrémentation) est donc aussi séquencée avant la cession. En C++11, seuls les valeur de calcul de l'opérande de droite a été séquencée avant la cession.
Dans les anciennes normes C++ et en C11, la définition de l'opérateur d'affectation texte se termine par le texte:
Sens que les effets secondaires dans les opérandes sont séquencé et donc sans doute un comportement indéfini s'ils utilisent la même variable.
Ce texte a été tout simplement supprimé en C++11, laissant quelque peu ambiguë. Est-il UB ou n'est-il pas? Cela a été précisé en C++17 où ils ont ajouté:
Comme une note de côté, de même les normes les plus anciennes, c'était très clair, par exemple de la C99:
Fondamentalement, en C11/C++11, ils l'ont foiré quand ils ont retiré ce texte.