39. Lvalues et Rvalues▲
NDT Dans cette traduction, on a choisi de ne pas traduire les termes « rvalue » et « lvalue », mais il se peut qu'à certain endroit, « valeur à droite (du égal) » et « valeur à gauche (du égal) » soient utilisées occasionnellement. Consultez la dernière partie du chapitre pour plus d'explication sur ces noms.
La valeur de chaque expression est, soit une lvalue soit une rvalue. Une façon simple de différencier les deux est de voir les lvalues comme de véritables variables (y compris les éléments de tableau et de tableaux associatifs) et les rvalues comme des résultats temporaires d'expressions (y compris les littéraux).
Comme démonstration, la première expression writeln ci-après n'utilise que des lvalues et l'autre que des rvalues :
import
std.stdio;
void
main
(
)
{
int
i;
immutable
(
int
) imm;
auto
tab =
[ 1
];
auto
ta =
[ 10
: "dix"
];
/* tous les arguments suivants sont des lvalues. */
writeln
(
i, // variable mutable
imm, // variable immutable
tab, // tableau
tab[0
], // élément de tableau
ta[10
]); // élément de tableau associatif
// etc.
enum
message =
"Bonjour"
;
/* tous les arguments suivants sont des rvalues. */
writeln
(
42
, // un littéral
message, // une constante manifeste
i +
1
, // une valeur temporaire
calculer
(
i)); // valeur de retour d'une fonction
// etc.
}
int
calculer
(
int
i)
{
return
i *
2
;
}
39-1. Les limitations des rvalues▲
Comparées aux lvalues, les rvalues présentent les trois limitations suivantes.
39-1-1. Les rvalues n'ont pas d'adresse▲
Une lvalue peut se trouver à un emplacement dans la mémoire, une rvalue non.
Par exemple, il est impossible de prendre l'adresse de l'expression rvalue a + b dans le programme suivant :
import
std.stdio;
void
main
(
)
{
int
a;
int
b;
readf
(
" %s "
, &
a); // compile
readf
(
" %s "
, &(
a +
b)); // Erreur: ne compile pas
}
Erreur : a + b n'est pas une lvalue
39-1-2. Les rvalues ne peuvent se voir affecter de nouvelles valeurs▲
Il est possible d'affecter une nouvelle valeur à une lvalue si elle est mutable. Ce n'est pas possible pour les rvalues :
a =
1
; // compile
(
a +
b) =
2
; // Erreur: ne compile pas
Sortie :
Erreur : a + b n'est pas une lvalue
39-2. Les rvalues ne peuvent pas être passées par référence à des fonctions▲
Une lvalue peut être passée à une fonction qui prend un argument par référence. Ce n'est pas possible avec une rvalue :
void
incrementerDeDix
(
ref int
valeur)
{
value +=
10
;
}
// ...
incrementerDeDix
(
a); // compile
incrementerDeDix
(
a +
b); // Erreur: ne compile pas
Sortie :
Erreur : la fonction incrementerDeDix
(
ref int valeur) n'est pas appelable avec les types d'
arguments (
int)
La raison principale de cette limitation est que les fonctions prenant un paramètre ref pourraient stocker la référence pour une utilisation ultérieure, à un moment où la rvalue ne serait plus disponible.
À la différence de langages comme C++, en D une rvalue ne peut pas être passée à une fonction même si cette fonction ne modifie pas l'argument (par exemple en prenant une référence à const) :
void
afficher
(
ref const
(
int
) valeur)
{
writeln
(
valeur);
}
// ...
afficher
(
a); // compile
afficher
(
a +
b); // Erreur: ne compile pas
Sortie :
Erreur : la fonction afficher
(
ref const
(
int) valeur) n'est pas appelable en utilisant les types d'
arguments (
int)
39-3. Les paramètres auto ref pour accepter à la fois des rvalues et des lvalues▲
Comme mentionné dans le chapitre précédent, les paramètres auto ref des fonctions templates peuvent prendre à la fois des lvalues et des rvalues.
Quand l'argument est une lvalue, auto ref signifie par référence. À l'inverse, puisque les rvalues ne peuvent pas être passées par référence, quand l'argument est une rvalue, auto ref signifie par copie. Pour que le compilateur puisse générer les différents codes pour ces deux cas de figure, la fonction doit être un template.
Nous verrons les templates dans un chapitre ultérieur. Pour l'instant, veuillez accepter que les parenthèses vides surlignées ci-après signifient que la définition de fonction suivante est une fonction template.
Comme mentionné dans le commentaire du code ci-avant, la mutation du paramètre peut ne pas être observable par l'appelant. Pour cette raison, auto ref est essentiellement utilisé dans des situations où le paramètre n'est pas modifié ; la plupart du temps comme auto ref const.
39-4. Terminologie▲
Les noms lvalue et rvalue ne représentent pas précisément les caractéristiques de ces deux types de valeurs. Les initiales l et r viennent de l'anglais left et right, faisant référence au côté gauche et au côté droit d'une expression de l'opérateur d'affectation :
- en partant du principe qu'elle est mutable, une lvalue peut être l'expression du côté gauche d'une opération d'affectation ;
- une rvalue ne peut pas être le côté gauche d'une opération d'affectation.
Les termes « valeur gauche » et « valeur droite » sont trompeurs, car en général, lvalues et rvalues peuvent à la fois se trouver de chaque côté d'une opération d'affectation :
// rvalue 'a + b' sur la gauche, lvalue 'a' sur la droite :
tableau[a +
b] =
a;