web-dev-qa-db-fra.com

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 cette expression drôle et alambiquée (les phrases en italique sont tirées du sujet ci-dessus * sourire *):

i += ++i;

Nous disons que cela invoque un comportement indéfini. Je suppose qu'en disant cela, nous supposons implicitement que type of i est l'un des types intégrés.

Que faire 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 cet article (voir ci-dessous). Cela invoquerait-il toujours un comportement indéfini?

Si oui, pourquoi? N'est-ce pas équivalent à écrire i.operator+=(i.operator++()); ou même syntaxiquement plus simple i.add(i.inc());? Ou invoquent-ils 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 rappeler la règle d'or: ne expression ne peut modifier la valeur d'un objet qu'une seule fois entre des "points de séquence" consécutifs . Et si i += ++i Est une expression, elle doit alors invoquer un comportement indéfini. Si donc, ses équivalents i.operator+=(i.operator++()); et i.add(i.inc()); doivent également invoquer un comportement indéfini qui semble être faux! (pour autant que je comprends)

Ou, i += ++i N'est pas une expression pour commencer? Si oui, alors qu'est-ce que c'est et quelle est la définition de expression?

Si c'est une expression, et en même temps, son comportement est aussi bien défini, alors cela implique que le nombre de points de séquence associés à une expression dépend en quelque sorte du type des opérandes impliqués dans l'expression. Suis-je correct (même partiellement)?


Au fait, qu'en est-il 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 également en tenir compte dans votre réponse (si vous connaissez son comportement à coup sûr). :-)


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;
        }
};
84
Nawaz

Il ressemble au 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 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 la i.operator ++() comme paramètre de operator += A un point de séquence après son évaluation. En bref, parce que les opérateurs surchargés sont des fonctions, les règles de séquençage normales s'appliquent.

Grande question, au fait! J'aime vraiment la façon dont vous me forcez à comprendre toutes les nuances d'une langue que je pensais déjà connaître (et pensais que je pensais savoir). :-)

48
templatetypedef

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 );

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 les fonctions comprennent des points de séquence.

D'autre part, 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ée en premier. Il se pourrait que ++i est évalué, transmis à operator[] (ou la version brute) pour y récupérer l'objet, puis la valeur de i est passée à celle-ci (ce qui est après que j'ai été incrémenté). D'un autre côté, peut-être que ce dernier côté est évalué en premier, stocké pour une affectation ultérieure, puis le ++i la pièce est évaluée.

11
Edward Strange

Je pense que c'est bien défini:

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

"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 fonction13). Plusieurs contextes en C++ provoquent l'évaluation d'un appel de fonction, même si aucune syntaxe d'appel de fonction correspondante n'apparaît dans l'unité de traduction. [ Exemple : l'évaluation d'une nouvelle expression appelle une ou plusieurs fonctions d'allocation et de constructeur; voir 5.3.4. Pour un autre exemple, l'invocation d'une fonction de conversion ( 12.3.2) peut survenir dans des contextes dans lesquels aucune syntaxe d'appel de fonction n'apparaît - exemple de fin ] La séquence pointe sur entrée-fonction et sortie-fonction ( comme décrit ci-dessus) sont des caractéristiques des appels de fonction tels qu'évalués, quelle que soit la syntaxe de l'expression qui appelle la fonction. "

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

8
Matthew Flaschen

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

a[++i] = i;

Cas 1:

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

a [++ i] = i n'est pas aussi chanceux en supposant que a est votre type de tableau de base, ou même un utilisateur défini . 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.

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

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

Cas 2:

Si a est un objet de type défini par l'utilisateur qui surcharge le operator[], Il y a encore deux cas.

  1. Si le type de retour de la fonction operator[] Surchargée est de type intégré, alors à nouveau a[++i]=i Invoque un comportement indéfini ou le résultat n'est pas spécifié.
  2. Mais si le type de retour de la fonction operator[] Surchargée est de type défini par l'utilisateur, le comportement de a[++i] = i Est bien défini (pour autant que je sache), car dans ce cas a[++i]=i équivaut à écrire a.operator[](++i).operator=(i); qui est identique à a[++i].operator=(i);. Autrement dit, l'affectation operator= Est invoquée sur l'objet renvoyé de a[++i], Qui semble très bien défini, car au moment où a[++i] revient, ++i ont déjà été évalués, puis l'objet retourné appelle la fonction operator= en passant la valeur mise à jour de i comme argument. Notez qu'il existe un point de séquence entre ces deux appels . Et la syntaxe garantit qu'il n'y a pas de concurrence entre ces deux appels, et operator[] Serait invoqué en premier, et consécutivement, l'argument ++i Passé dedans, serait également évalué en premier.

Considérez ceci comme someInstance.Fun(++k).Gun(10).Sun(k).Tun(); dans lequel chaque appel de fonction consécutif renvoie un objet d'un 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.

Corrigez-moi si j'ai tort, s'il-vous plait. :-)

6
Nawaz