Considérez ce sujet comme une suite du sujet suivant:
Épisode précédent
Comportement et points de séquence non définis
Revisitons 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 présume que lorsque vous dites cela, nous supposons implicitement que le type de 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
défini plus loin dans ce post (voir ci-dessous). Invoquerait-il toujours un comportement indéfini?
Si oui, pourquoi? N'est-ce pas équivalent à l'écriture i.operator+=(i.operator++());
ou même syntaxiquement plus simple i.add(i.inc());
? Ou, 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. Rappelez-vous la règle empirique: une expression ne peut modifier la valeur d'un objet qu'une seule fois entre "points de séquence consécutifs" . Et s'il i += ++i
s'agit d'une expression, elle doit invoquer un comportement indéfini. Si tel est le cas, ses équivalents i.operator+=(i.operator++());
et i.add(i.inc());
doit également invoquer un comportement indéfini qui semble être faux! (pour autant que je sache)
Ou i += ++i
n'est-ce pas une expression pour commencer? Si oui, qu'est-ce que c'est et quelle est la définition de l' expression ?
S'il s'agit d'une expression, et en même temps, son comportement est également bien défini, cela implique que le nombre de points de séquence associés à une expression dépend en quelque sorte du type d'opérandes impliqués dans l'expression. Ai-je raison (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 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;
}
};
s
est un type défini par l'utilisateur!)Réponses:
Cela 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:
Cela indiquerait, par exemple, que le
i.operator ++()
paramètreoperator +=
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, au fait! J'aime beaucoup la façon dont vous me forcez à comprendre toutes les nuances d'un langage que je pensais déjà connaître (et pensais que je pensais savoir). :-)
la source
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 );
la source
Comme d'autres l'ont dit, votre
i += ++i
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'un autre côté, il
a[++i] = i
n'est pas si chanceux de supposer quea
c'est votre type de tableau de base, ou même celui défini par l'utilisateur. Le problème que vous avez ici est que nous ne savons pas quelle partie de l'expression contenanti
est évaluée en premier. Il se peut que ce++i
soit évalué, transmis àoperator[]
(ou à la version brute) afin de récupérer l'objet là-bas, puis la valeur dei
est passée à cela (ce qui est après l'incrémentation de i). D'autre part, peut-être que ce dernier côté est évalué en premier, stocké pour une affectation ultérieure, puis la++i
pièce est évaluée.la source
++i
et la lecture dui
RHS de l'affectation, alors l'ordre serait non spécifié. Étant donné que l'un des ordres autorisés effectue ces deux opérations sans point de séquence intermédiaire, le comportement n'est pas défini.a
et intégréi
.Je pense que c'est bien défini:
À partir du projet de norme C ++ (n1905) §1.9 / 16:
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 fonction d'incrémentation (
i.operator ++()
) mais avant l'appel d'assignation composée (i.operator+=
).la source
Bien. Après avoir parcouru les réponses précédentes, j'ai repensé à ma propre question, en particulier à cette partie à laquelle seul Noah a tenté de répondre mais je ne suis pas complètement convaincu avec lui.
Cas 1:
Si
a
est un tableau de type intégré. Alors ce que Noé a dit est correct. C'est,a[++i]=i
Invoque donc 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 à moi.Cas 2:
Si
a
est un objet de type défini par l'utilisateur qui surcharge leoperator[]
, il y a là encore deux cas.operator[]
fonction surchargée est un type intégré, alors à nouveaua[++i]=i
invoque undefined-behavior ou le résultat n'est pas spécifié.operator[]
fonction est de type défini par l' utilisateur, le comportementa[++i] = i
est bien défini (pour autant que je comprends), puisque dans ce casa[++i]=i
est équivalent à l' écriturea.operator[](++i).operator=(i);
qui est la même que,a[++i].operator=(i);
. C'est-à-dire que l'affectationoperator=
est invoquée sur l' objet retourné dea[++i]
, qui semble être très bien défini, car au moment où lesa[++i]
retours++i
ont déjà été évalués, l' objet retourné appelle laoperator=
fonction en lui passant la valeur mise à jour dei
en 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, etoperator[]
serait invoqué en premier, et consécutivement, l'argument qui y est++i
passé serait également évalué en premier.Considérez cela 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. :-)
la source
k++
et nek
sont pas séparés par des points de séquence. Ils peuvent tous deux être évalués avant l'unSun
ou l' autreFun
. Le langage exige seulement que ceFun
soit évalué avantSun
, pas queFun
les arguments de soient évalués avantSun
les arguments de. J'explique en quelque sorte la même chose sans pouvoir fournir de référence, donc nous n'allons pas progresser à partir d'ici.Sun
exécution, maisFun
l'argument de++k
peut être évalué avant ou après cela. Il y a des points de séquence avant et après l'Fun
exécution, maisSun
l'argument dek
peut être évalué avant ou après cela. Par conséquent, un cas possible est que les deuxk
et++k
sont évalués avant l'unSun
ou l' autre ouFun
sont évalués, et donc les deux sont avant les points de séquence d'appel de fonction, et il n'y a donc pas de point de séquence séparantk
et++k
.eat(i++);drink(10);sleep(i);
? ... même maintenant, vous pourriez direi++
peut être évalué avant ou après cela?k
et++k
. Dans l'exemple manger / boire, il y a un point de séquence entrei
eti++
.eat()
etsleep()
existe un ou plusieurs points de séquence, mais entre les arguments, il n'y en a même pas un. Comment les arguments de deux appels de fonction séparés par des points de séquence peuvent-ils appartenir aux mêmes points de séquence?