IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Cours complet pour apprendre à programmer en D


précédentsommairesuivant

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 :

 
Sélectionnez
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 :

 
Sélectionnez
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 :

 
Sélectionnez
a = 1; // compile
(a + b) = 2; // Erreur: ne compile pas

Sortie :

 
Sélectionnez
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 :

 
Sélectionnez
void incrementerDeDix(ref int valeur)
{
    value += 10;
}
 
// ...
 
    incrementerDeDix(a);     // compile
    incrementerDeDix(a + b); // Erreur: ne compile pas

Sortie :

 
Sélectionnez
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) :

 
Sélectionnez
void afficher(ref const(int) valeur)
{
    writeln(valeur);
}
 
// ...
 
    afficher(a);        // compile
    afficher(a + b);    // Erreur: ne compile pas

Sortie :

 
Sélectionnez
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.

Image non disponible

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 :

 
Sélectionnez
// rvalue 'a + b' sur la gauche, lvalue 'a' sur la droite :
tableau[a + b] = a;

précédentsommairesuivant