chapter lvalue_rvalue by Olivier
This commit is contained in:
parent
29f9083782
commit
0b2ee8f3ee
|
@ -0,0 +1,166 @@
|
|||
[set
|
||||
title = "Lvalues et Rvalues"
|
||||
partAs = chapitre
|
||||
translator = "Olivier Pisano"
|
||||
]
|
||||
|
||||
[comment <<< Note laissée par Raphaël Jake >>>]
|
||||
|
||||
[p 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 occasionellement. 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 tableaux 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 [c writeln] ci-après n'utilise que des lvalues et l'autre que des rvalues~ :
|
||||
|
||||
[code=d <<<
|
||||
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;
|
||||
}
|
||||
>>>]
|
||||
|
||||
[ = Les limitations des rvalues
|
||||
Comparées aux lvalues, les rvalues présentent les trois limitations suivantes~ :
|
||||
|
||||
[ = 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 [c a + b] dans le programme suivant~ :
|
||||
|
||||
[code=d <<<
|
||||
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
|
||||
]
|
||||
|
||||
[ = 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
|
||||
|
||||
Erreur : a + b n'est pas une lvalue
|
||||
]
|
||||
]
|
||||
|
||||
[ = 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~ :
|
||||
|
||||
[code=d <<<
|
||||
void incrementerDeDix(ref int valeur)
|
||||
{
|
||||
value += 10;
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
incrementerDeDix(a); // compile
|
||||
incrementerDeDix(a + b); // Erreur: ne compile pas
|
||||
>>>]
|
||||
|
||||
[output <<<
|
||||
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 [c 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 à [c const])~ :
|
||||
|
||||
[code=d <<<
|
||||
void afficher(ref const(int) valeur)
|
||||
{
|
||||
writeln(valeur);
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
afficher(a); // compile
|
||||
afficher(a + b); // Erreur: ne compile pas
|
||||
>>>]
|
||||
|
||||
[output <<<
|
||||
Erreur : la fonction afficher(ref const(int) valeur) n'est pas appelable en utilisant les types d'arguments (int)
|
||||
>>>]
|
||||
]
|
||||
|
||||
[ = 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 être 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.
|
||||
|
||||
[code=d <<<
|
||||
void incrementerDeDix()(auto ref int valeur)
|
||||
{
|
||||
/* AVERTISSEMENT: Le paramètre peut être une copie si l'argument est
|
||||
une rvalue. Cela veut dire que la modification suivante peut ne pas
|
||||
être visible par l'appelant. */
|
||||
|
||||
valeur += 10;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
|
||||
incrementerDeDix(a); // lvalue; passée par référence
|
||||
incrementerDeDix(a + b); // rvalue; passée par valeur
|
||||
}
|
||||
>>>]
|
||||
|
||||
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 de temps comme [c auto ref const].
|
||||
]
|
||||
|
||||
[ = 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ôte 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ôte 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~ :
|
||||
|
||||
[code=d <<<
|
||||
// rvalue 'a + b' sur la gauche, lvalue 'a' sur la droite :
|
||||
tableau[a + b] = a;
|
||||
>>>]
|
||||
]
|
2
map.txt
2
map.txt
|
@ -35,6 +35,7 @@ enum enum
|
|||
functions fonctions
|
||||
const_and_immutable const_et_immutable
|
||||
function_parameters parametres_de_fonctions
|
||||
lvalue_rvalue lvalue_rvalue
|
||||
lazy_operators operateurs_paresseux
|
||||
main main
|
||||
exceptions exceptions
|
||||
|
@ -50,3 +51,4 @@ parameter_flexibility flexibilite_parametres
|
|||
function_overloading surcharge_fonctions
|
||||
member_functions fonctions_membres
|
||||
const_member_functions fonctions_membres_const
|
||||
special_functions functions_speciales
|
||||
|
|
Loading…
Reference in New Issue