Pourquoi le premier retourne-t-il une référence?
int x = 1;
int y = 2;
(x > y ? x : y) = 100;
Alors que le second ne fonctionne pas?
int x = 1;
long y = 2;
(x > y ? x : y) = 100;
En fait, le second n'a pas compilé du tout - "pas la valeur restante de l'affectation".
Les expressions n'ont pas de types de retour, elles ont un type et - comme cela est connu dans la dernière norme C++ - une catégorie de valeur.
Une expression conditionnelle peut être une lvalue ou une rvalue . C'est sa catégorie de valeur. (Il s'agit en quelque sorte d'une simplification, dans C++11
nous avons lvalues, xvalues et prvalues.)
En termes très larges et simples, une lvalue fait référence à un objet en mémoire et une rvalue est juste une valeur qui ne peut pas nécessairement être attachée à un objet en mémoire.
Une expression d'affectation attribue une valeur à un objet de sorte que la chose à affecter doit être une lvalue .
Pour une expression conditionnelle (?:
) pour être une lvalue (encore une fois, en termes larges et simples), les deuxième et troisième opérandes doivent être lvaleurs du même type. En effet, le type et la catégorie de valeur d'une expression conditionnelle sont déterminés au moment de la compilation et doivent être appropriés, que la condition soit vraie ou non. Si l'un des opérandes doit être converti en un type différent pour correspondre à l'autre, alors l'expression conditionnelle ne peut pas être une valeur l car le résultat de cette conversion ne serait pas être une lvalue .
Références ISO/IEC 14882: 2011:
3.10 [basic.lval] Lvalues et rvalues (à propos des catégories de valeurs)
5.15 [expr.cond] Opérateur conditionnel (règles concernant le type et la catégorie de valeur d'une expression conditionnelle)
5.17 [expr.ass] Opérateurs d'affectation et d'affectation composée (exigence que le l.h.s. d'une affectation soit une valeur l modifiable)
Le type de ternaire ?:
expression est le type commun de ses deuxième et troisième arguments. Si les deux types sont identiques, vous obtenez une référence. S'ils sont convertibles entre eux, l'un est choisi et l'autre est converti (promu dans ce cas). Comme vous ne pouvez pas renvoyer une référence lvalue à une variable temporaire (la variable convertie/promue), son type est un type valeur.
Il ne peut pas renvoyer un lvalue car il devra implicitement promouvoir le type de x
pour qu'il corresponde au type de y
(puisque les deux côtés de :
ne sont pas du même type), et avec cela il faut créer un temporaire.
Expressions 5.17 Opérateurs d'affectation et d'affectation composée
5,17/3
Si les deuxième et troisième opérandes ont des types différents et que l'un ou l'autre a un type de classe (éventuellement qualifié par cv), une tentative est effectuée pour convertir chacun de ces opérandes en type de l'autre. Le processus pour déterminer si une expression d'opérande E1 de type T1 peut être convertie pour correspondre à une expression d'opérande E2 de type T2 est défini comme suit:
- Si E2 est une valeur l: E1 peut être converti pour correspondre à E2 si E1 peut être implicitement converti (article 4) au type "référence à T2", sous réserve de la contrainte que dans la conversion, la référence doit se lier directement (8.5.3 ) à E1.
- Si E2 est une valeur r, ou si la conversion ci-dessus ne peut pas être effectuée:
- si E1 et E2 ont un type de classe et que les types de classe sous-jacents sont identiques ou que l'un est une classe de base de l'autre: E1 peut être converti pour correspondre à E2 si la classe de T2 est du même type que, ou une classe de base de , la classe de T1 et la qualification cv de T2 est la même qualification cv que la qualification cv de T1, ou une qualification cv supérieure. Si la conversion est appliquée, E1 est remplacé par une valeur r de type T2 qui fait toujours référence à l'objet de classe source d'origine (ou à son sous-objet approprié). [ Remarque: en d'autres termes, aucune copie n'est effectuée. - end note] en copiant-initialisant un temporaire de type T2 à partir de E1 et en utilisant ce temporaire comme opérande converti.
Sinon (c'est-à-dire, si
E1
ou E2 a un type non classe, ou si les deux ont des types classe mais les classes sous-jacentes ne sont pas les mêmes ou l'une une classe de base de l'autre): E1 peut être converti pour correspondre à E2 si E1 peut être implicitement converti en le type que l'expression E2 aurait si E2 était converti en une valeur r (ou le type qu'elle a, si E2 est une valeur r).En utilisant ce processus, il est déterminé si le deuxième opérande peut être converti pour correspondre au troisième opérande, et si le troisième opérande peut être converti pour correspondre au deuxième opérande. Si les deux peuvent être convertis, ou l'un peut être converti mais la conversion est ambiguë, le programme est mal formé. Si aucun des deux ne peut être converti, les opérandes restent inchangés et une vérification supplémentaire est effectuée comme décrit ci-dessous. Si une seule conversion est possible, cette conversion est appliquée à l'opérande choisi et l'opérande converti est utilisé à la place de l'opérande d'origine pour le reste de cette section.
5,17/4
Si les deuxième et troisième opérandes sont des valeurs l et ont le même type, le résultat est de ce type et est une valeur l et c'est un champ binaire si le deuxième ou le troisième opérande est un champ binaire, ou si les deux sont des bits. des champs.
5,17/5
Sinon, le résultat est une valeur r. Si les deuxième et troisième opérandes n'ont pas le même type et ont l'un ou l'autre un type de classe (éventuellement qualifié par cv), la résolution de surcharge est utilisée pour déterminer les conversions (le cas échéant) à appliquer aux opérandes (13.3.1.2, 13.6) . Si la résolution de surcharge échoue, le programme est mal formé. Sinon, les conversions ainsi déterminées sont appliquées et les opérandes convertis sont utilisés à la place des opérandes d'origine pour le reste de cette section.