85 votes

Comportement indéfini et points de séquence rechargés

Considérez ce sujet comme une suite du sujet suivant :

Versement précédent
Comportement indéfini et points de séquence

Revoyons cela drôle et alambiqué (les phrases en italique sont tirées du sujet ci-dessus *smile* ) :

i += ++i;

Nous disons que cela invoque un comportement indéfini. Je présume que lorsque nous disons cela, nous supposons implicitement que type de i est l'un des types intégrés.

Et si le type de i est un type défini par l'utilisateur ? Disons que son type est Index qui est défini plus loin dans ce post (voir ci-dessous). Cela invoquerait-il encore un comportement non défini ?

Si oui, pourquoi ? N'est-il pas équivalent d'écrire i.operator+=(i.operator++()); ou même syntaxiquement plus simple i.add(i.inc()); ? Ou bien, invoquent-ils eux aussi un comportement indéfini ?

Si non, pourquoi pas ? Après tout, l'objet i est modifié deux fois entre des points de séquence consécutifs. Veuillez vous rappeler la règle de base : une expression ne peut modifier la valeur d'un objet qu'une seule fois entre deux "points de séquence" consécutifs. . Et si i += ++i est une expression, alors elle doit invoquer un comportement indéfini. Si c'est le cas, alors ses équivalents i.operator+=(i.operator++()); et i.add(i.inc()); doit aussi invoquer undefined-behavior ce qui semble être faux ! (d'après ce que je comprends)

Ou, i += ++i n'est pas un expression pour commencer ? Si c'est le cas, qu'est-ce que c'est et quelle est la définition de l'expression "l'eau" ? expression ?

Si c'est une expression, et en même temps, son comportement est également bien défini, alors cela implique que le nombre de points de séquence associés à une expression dépend d'une manière ou d'une autre de l'élément type des opérandes impliqués dans l'expression. Ai-je raison (même partiellement) ?


Au fait, que pensez-vous de cette expression ?

//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the subscript operator!

a[++i] = i; //Taken from the previous topic. But here type of `i` is Index.

Vous devez en tenir compte également dans votre réponse (si vous connaissez son comportement avec certitude) :-)


Est

++++++i;

bien défini en C++03 ? Après tout, c'est ça,

((i.operator++()).operator++()).operator++();

class Index
{
    int state;

    public:
        Index(int s) : state(s) {}
        Index& operator++()
        {
            state++;
            return *this;
        }
        Index& operator+=(const Index & index)
        {
            state+= index.state;
            return *this;
        }
        operator int()
        {
            return state;
        }
        Index & add(const Index & index)
        {
            state += index.state;
            return *this;
        }
        Index & inc()
        {
            state++;
            return *this;
        }
};

13 votes

+1 grande question, qui a inspiré de grandes réponses. Je pense que je devrais dire que c'est toujours un code horrible qui devrait être remanié pour être plus lisible, mais vous le savez probablement de toute façon :)

0 votes

Depuis quand s++ est identique à ++s ?

4 votes

La question est la suivante : qui a dit que c'était pareil ? ou qui a dit que ce n'était pas pareil ? Cela ne dépend-il pas de la façon dont vous les mettez en œuvre ? (Note : je suppose que le type de s est un type défini par l'utilisateur)

48voto

templatetypedef Points 129554

Il semble que le code

i.operator+=(i.operator ++());

Fonctionne parfaitement bien en ce qui concerne les points de séquence. La section 1.9.17 de la norme ISO C++ dit ceci à propos des points de séquence et de l'évaluation des fonctions :

Lors de l'appel d'une fonction (que la fonction soit en ligne ou non), il existe un point de séquence après l'évaluation de tous les arguments de la fonction (le cas échéant) qui a lieu avant l'exécution de toute expression ou instruction dans le corps de la fonction. Il existe également un point de séquence après la copie d'une valeur renvoyée et avant l'exécution de toute expression en dehors de la fonction.

Cela indiquerait, par exemple, que le i.operator ++() comme paramètre pour operator += a un point de séquence après son évaluation. En bref, comme les opérateurs surchargés sont des fonctions, les règles de séquencement normales s'appliquent.

Excellente question, d'ailleurs ! J'aime beaucoup la façon dont vous me forcez à comprendre toutes les nuances d'une langue que je pensais déjà connaître (et que je pensais connaître) :-)

12voto

http://www.eelis.net/C++/analogliterals.xhtml Les littéraux analogiques me viennent à l'esprit

  unsigned int c = ( o-----o
                     |     !
                     !     !
                     !     !
                     o-----o ).area;

  assert( c == (I-----I) * (I-------I) );

  assert( ( o-----o
            |     !
            !     !
            !     !
            !     !
            o-----o ).area == ( o---------o
                                |         !
                                !         !
                                o---------o ).area );

0 votes

Une question a été posée : ++++++i ; est-il bien défini dans C++03 ?

11voto

Crazy Eddie Points 23778

Comme d'autres l'ont dit, votre i += ++i L'exemple fonctionne avec le type défini par l'utilisateur puisque vous appelez des fonctions, et que les fonctions comprennent des points de séquence.

D'un autre côté, a[++i] = i n'est pas aussi chanceux en supposant que a est votre type de tableau de base, ou même un type défini par l'utilisateur. Le problème que vous avez ici est que nous ne savons pas quelle partie de l'expression contenant i est évalué en premier. Il se peut que ++i est évaluée et transmise à operator[] (ou la version brute) afin de récupérer l'objet à cet endroit, puis la valeur de l'option i lui est transmis (qui est après i a été incrémenté). D'autre part, il se peut que le dernier côté soit d'abord évalué, stocké pour une affectation ultérieure, puis que la fonction ++i est évaluée.

0 votes

Alors... le résultat est donc non spécifié plutôt que UB, puisque l'ordre d'évaluation des expressions est non spécifié ?

0 votes

@Philip : unspecified signifie que nous attendons du compilateur qu'il spécifie le comportement, alors que undefined n'impose pas une telle obligation. Je pense qu'il est indéfini ici, pour laisser aux compilateurs plus de place pour les optimisations.

0 votes

@Noah : J'ai également posté une réponse. Jetez-y un coup d'œil et faites-moi part de vos impressions :-)

8voto

Matthew Flaschen Points 131723

Je pense que c'est bien défini :

Extrait du projet de norme C++ (n1905) §1.9/16 :

"Il y a aussi un point de séquence après la copie d'une valeur retournée et avant l'exécution de toute expressions en dehors de la fonction13) . Plusieurs contextes en C++ provoquent l'évaluation d'un appel de fonction, même même si aucune syntaxe d'appel de fonction correspondante n'apparaît dans l'unité de traduction. unité. [ Exemple L'évaluation d'une nouvelle expression invoque une ou plusieurs fonctions d'allocation et de construction ; voir 5.3.4. Autre exemple l'invocation d'une fonction de conversion (12.3.2) peut survenir dans des contextes où aucune fonction dans des contextes où aucune syntaxe d'appel de fonction n'apparaît. - exemple de fin La séquence pointe sur l'entrée et à la sortie de la fonction (comme décrits ci-dessus) sont des caractéristiques des appels de fonction tels qu'évalués, quel que soit la syntaxe de l'expression que appelle la fonction pourrait être. "

Notez la partie que j'ai mise en gras. Cela signifie qu'il y a bien un point de séquence après l'appel de la fonction d'incrémentation ( i.operator ++() ) mais avant l'appel de l'affectation composée ( i.operator+= ).

6voto

Nawaz Points 148870

Très bien. Après avoir parcouru les réponses précédentes, j'ai repensé à ma propre question, en particulier à la partie à laquelle seul Noah a tenté de répondre. réponse mais je ne suis pas complètement convaincu par lui.

a[++i] = i;

Cas 1 :

Si a est un tableau de type intégré. Alors ce que Noah a dit est correct. C'est à dire,

a[++i] = i n'est pas si chanceux en supposant que a est votre type de tableau de base, ou même une définition de l'utilisateur . Le problème que vous avez ici est que nous ne savons pas quelle partie de l'expression contenant i est évaluée en premier.

Alors a[++i]=i invoque un comportement indéfini, ou le résultat est non spécifié. Quoi qu'il en soit, ce n'est pas bien défini !

PS : Dans la citation ci-dessus, percée est bien sûr le mien.

Cas 2 :

Si a est un objet de type défini par l'utilisateur qui surcharge la fonction operator[] mais là encore, il y a deux cas.

  1. Si le type de retour de la surcharge operator[] est un type intégré, alors encore a[++i]=i invoque un comportement non défini ou le résultat est non spécifié.
  2. Mais si le type de retour de la surcharge operator[] est un type défini par l'utilisateur, alors le comportement de la fonction a[++i] = i est bien défini (d'après ce que je comprends), puisque dans ce cas-ci a[++i]=i est équivalent à écrire a.operator[](++i).operator=(i); qui est le même que, a[++i].operator=(i); . C'est-à-dire que l'affectation operator= est invoqué sur le a retourné objet de a[++i] qui semble être très bien défini, puisqu'au moment où a[++i] retours, ++i ont déjà été évaluées, puis les a retourné appels d'objets operator= en passant la valeur mise à jour de i en tant qu'argument. Notez qu'il y a un point de séquence entre ces deux appels . Et la syntaxe veille à ce qu'il n'y ait pas de concurrence entre ces deux appels, et operator[] serait invoqué en premier, et consécutivement, l'argument ++i qui y sont passés, seraient également évalués en premier.

Pensez-y comme someInstance.Fun(++k).Gun(10).Sun(k).Tun(); dans lequel chaque appel de fonction consécutif renvoie un objet d'un certain type défini par l'utilisateur. Pour moi, cette situation ressemble plus à ceci : eat(++k);drink(10);sleep(k) car dans les deux situations, il existe un point de séquence après chaque appel de fonction.

Veuillez me corriger si je me trompe :-)

0 votes

Vous avez manqué le point. Le problème est qu'il n'est pas spécifié si le i ou l'expression ++i est évaluée en premier ; si i ou a sont de type défini par l'utilisateur ou non .

0 votes

@Philip : J'ai ajouté quelques explications à mon post. Veuillez lire les dernières lignes !

0 votes

@Nawaz : en someInstance.Fun(++k).Gun(10).Sun(k).Tun(); il est possible que le k sur Sun(k) est évalué avant le ++k sur Fun(++k) . Une fois que cela est fait, le compilateur devra conserver le résultat quelque part, car Fun() doit être évaluée avant Sun() parce que Sun() dépend de Fun() de la sortie. Cette dépendance ne pas s'étendent aux arguments de Sun() et Fun() et c'est là que se situe votre problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X