web-dev-qa-db-fra.com

Pourquoi ++ x est-il une lvalue et x ++ est-il une rvalue?

J'ai donc lu sur lvalue et rvalues ​​et je suis un peu confus quant à la différence entre ++x et x++ en ce qui concerne cette catégorisation.

Pourquoi ++x une valeur et x++ une valeur?

92
samuelnj

_++x_ renvoie une référence à l'objet que vous avez incrémenté, où _x++_ renvoie une copie temporaire de l'ancienne valeur de x.

Au moins, ce serait la manière "normale" de mettre en œuvre ces opérateurs par convention. Et tous les types intégrés fonctionnent de cette façon. Et si vous avez entendu parler de lvalues ​​/ rvalues, vous verrez que, puisque l'opérateur de préfixe renvoie l'objet nommé lui-même, il s'agirait d'une valeur de lvalue, l'opérateur de postfix renvoyant une copie d'un temporaire local, qui serait alors qualifié de rvalue. .

Note: De plus, nous avons maintenant des prvalues, des xvalues, etc., donc c'est techniquement un peu plus compliqué de nos jours. Regardez ici pour plus d'informations.

108
Carl

C++ (par opposition à C) est un langage consacré préservant la valeur: il s'efforce de préserver minutieusement la "valeur" d'une expression chaque fois que cela est possible.

  • Il est très facile de conserver le "lvalueness" de pré-incrémentation: incrémentez simplement l'opérande et renvoyez-le sous forme de lvalue. Terminé. La valeur lvalue retournée contiendra exactement le résultat qu'elle est supposée contenir: la nouvelle valeur (incrémentée) de l'opérande.

  • Et dans le même temps, il est pratiquement impossible de préserver la "valeur" de post-incrémentation: par définition, le résultat de la post-incrémentation est l'ancienne valeur (d'origine) de l'opérande. Si vous essayez de renvoyer une lvalue à partir de la post-incrémentation, vous devez vous assurer simultanément de deux choses: 1) la lvalue est incrémentée, 2) le code appelant voit la valeur old lorsqu'il examine cette même lvalue (!). Cette combinaison d'exigences est tellement contradictoire qu'il est fondamentalement impossible de l'implémenter dans un modèle d'objet C++.

    Afin d'implémenter le comportement post-incrément approprié, il faut s'assurer que le code appelant ne regarde pas directement l'opérande, mais plutôt dans un "proxy" conceptuel ou physique qui fait en sorte que le code appelant "voit" l'ancienne valeur de l'opérande. Ce proxy peut être un objet temporaire contenant l'ancienne valeur. Ou ce proxy peut être quelque chose qui génère l'ancienne valeur à la volée en soustrayant 1 de la nouvelle valeur. Dans tous les cas, ce proxy empêche le code d’appel d’accéder à la lvalue d’origine.

C'est pourquoi C++ profite de l'opportunité facilement réalisable de préserver "la valeur" du pré-incrément, mais concède l'impossibilité d'obtenir le même résultat avec le post-incrément. En cas de post-incrémentation, il n’est tout simplement pas rentable de s’écarter du comportement classique en C, qui tend à écarter "l’évaluation" rapidement et avec bonheur.

45
AnT

Peut-être que noter le fonctionnement dans le pseudo-code des opérateurs pour int le rend plus clair:

préfixe:

int& Prefix(int& val)
{
  int& value = val;
  value += 1;
  return value;
}

postfixe:

int Postfix(int& val)
{
  int oldValue = val;
  val += 1;
  return oldValue; 
}

La différence réside dans ce qui est renvoyé par les deux opérateurs, un par valeur et un par référence.

29
Sombrero Chicken

Pourquoi ++ x est-il une valeur?

Je suppose que c'est une valeur parce que ça peut l'être.

++x on peut penser à

((x = x + 1), x)  // Increment x. Then evaluate to the value of x

ou

((x += 1), x)    //  Increment x. Then evaluate to the value of x

Il est logique d’évaluer ++x à une valeur.

x ++ une valeur?

Parce que ça ne peut pas être une valeur.

x++ on peut penser à

((unnamed_variable = x), (x = x + 1), unnamed_variable)

ou

((unnamed_variable = x), (x += 1), unnamed_variable)

x++ évalue à la valeur de avant x est incrémenté. Puisqu'aucune variable ne peut stocker cette valeur, il ne peut s'agir d'une valeur lvalue.

14
R Sahu

Pour les types intégrés, tel que int, x++ donne l'ancienne valeur de x. Il n'y a pas de stockage associé à cela, il serait donc impossible que l'expression soit une valeur.

En C, ++x a donné la nouvelle valeur de x (et n'était pas une valeur). Puisque l'objet x contient en fait la même valeur, il est possible que ++x désigne l'objet x (c'est-à-dire une valeur lvalue). Ceci est une fonctionnalité ajoutée par rapport à une valeur rvalue, et le concepteur de C++ a décidé qu'il s'agirait d'une amélioration du langage.

Pour les types de classe, il est possible de surcharger n'importe quel opérateur de telle sorte que son utilisation puisse générer une valeur lvalue, xvalue ou prvalue. Cependant, il est considéré comme un bon style de faire en sorte que les opérateurs surchargés aient une sémantique similaire à celle des opérateurs intégrés. C'est pourquoi il est normal que les utilisateurs surchargent ++x pour générer une valeur et surcharger x++ pour obtenir une valeur. .

11
M.M

Tout d'abord, une "lvalue" est une expression légale du côté gauche (le "l" dans "lvalue") d'une affectation. Cela signifie qu'il représente une adresse dont le contenu peut être modifié par l'affectation. (Le standard C appelle une telle chose un "objet".) C'est pourquoi, par exemple, les expressions d'opérateur infixes et les appels de fonction ne sont pas des valeurs.

"rvalue" est un terme idiot; toute expression bien formée est légale à droite d'une affectation (en laissant de côté les problèmes de conversion de type).

La norme dit que, par exemple:

i = ++i + 1;
a[i++] = i;

..are les deux "expressions d'instruction non définies", ce qui signifie que leur mise en œuvre (et donc leur comportement) n'est pas normalisée.

Le sens commun de "++ i" est "incrémente la valeur de l'objet i et évalue le résultat". De même, "i ++" signifie "évalue la valeur actuelle de l'objet i et incrémente ensuite la valeur de l'objet". Ni dit ce qui se passe lorsque "++ i" ou "i ++" est utilisé en tant que lvalue.

La norme dit, à propos des expressions: "Les calculs de valeur des opérandes d'un opérateur sont séquencés avant le calcul de la valeur du résultat de l'opérateur." C'est en fait ambigu pour "++ i", car le calcul de la valeur de l'opérande est affecté par le calcul de la valeur de l'opérateur.

Ce qui a le plus de sens pour moi est la suivante:

i = 5;
++i = i + 1;   /*i is set to 6*/
i++ = 1;       /*i is set to 1*/
++i = ++i + 1; /*i is set to 8, but problematic*/
i++ = i++ + 1; /*i is set to 8, but problematic*/
++i = i++ + 1; /*i is set to 8, but problematic*/

Mon conseil: n'utilisez pas d'expressions si difficiles à comprendre!

9
Stephen F. Heffner