Question sur les references

Le problème exposé dans ce sujet a été résolu.

Bonjour… Comme tjr je suis le cour de ce site, partie sur les fonctions. En lisant j’ai fait quelques notes sur la lecture du cours, notes qui reflètent ma compréhension et qui peut-être mal assimiler ou mal interpréter. Je vous laisse le soin de me corriger, voici ces notes :

Il ne faut pas lier une référence non-constante a un littéral invariable et qui n’existe nulle part dans la mémoire.
Lier une référence constante a un littéral est un code valide en C++. A la vue du mot clé const, le compilateur va créer un objet temporaire anonyme qui va contenir la valeur de la littéral en question. Il ne faut jamais renvoyer une référence sur une variable locale.

Et comme le compilateur ne prévient pas en cas ou il y a un comportement indetermine, je me demandais si l’on peut déclarer dans la fonction main, une référence constante dans la cible est le type de retour d’une fonction appeler dans main(autrement dit la cible est une variable locale). Comme c’est le cas dans le code ci-dessous :

#include <iostream>

int fonction ()
{
    int variable { 10 };
    return variable;
}

int main ()
{
    int const & reference_variable { fonction () };
    std::cout << reference_variable << std::endl;
    return 0;
}

Est-ce que ce code est correcte et valide ? Ou bien cela va-t-il engendrer un comportement indetermine. Ou bien est-ce que cela aura le meme effet qu’une copie, comme si on faisait

int variable { fonction () };

Je crois que le mot clé const va allouer une case mémoire qui va contenir une copie de return-value de la fonction avant que return-value disparaisse… est-ce que je me trompe ?

Besoin qu’on purge mes doutes…

+0 -0

Est-ce que ce code est correcte et valide ?

Réponse simple : oui.


Réponse simple mais plus détaillée.

Je ne sais pas si ce détail très particulier sur la durée de vie des temporaires est dans le cours. Normalement, un temporaire a la durée de vie de l’expression (quand l’expression est finie, le temporaire est détruit).

Par exemple, si tu écris :

void foo(int const& i) {
    std::cout << i << std::endl; // #1
}

int main() {
    // #2
    foo(1234); // #3
    // #4
}

Le temporaire "1234" de la ligne #3 n’existe pas encore a la ligne #2 et sera détruit a la ligne #4. Il existe pendant l’exécution de la ligne #3 uniquement. Mais cela implique qu’il existe aussi dans les sous-expressions utilisées dans la ligne #3, en particulier l’appel de fonction "foo" et a la ligne #1.

Si on déroule linéairement l’exécution de ce code, on pourra écrire :

  • exécution de ligne #2
  • exécution de la ligne #3

    • création de l’objet temporaire "1234"
    • appelle de la fonction "foo"

      • exécution de la ligne #1
    • destruction de l’objet temporaire "1234"
  • exécution de la ligne #4

A la vue du mot clé const, le compilateur va créer un objet temporaire anonyme

Tout ça pour expliquer que la durée de vie du temporaire est complètement indépendant de l’utilisation de const en fait. Le compilateur ne crée pas un temporaire parce qu’il y a const.

Par contre, le temporaire une littérale est toujours de type constant. Par exemple, la littérale 1234 sera de type int const et pas int. Or, il n’est pas possible de faire un cast implicit d’un const (le temporaire) vers un non const (une référence).

C’est pour cela que le code suivant est interdit, ce n’est pas un probleme de durée de vie du temporaire, c’est un probleme de respect des const.

void foo(int & i);
void bar(int const& i);

int main() {
    foo(1234); // erreur, cast de "const int" vers "int &"
    bar(1234); // ok, cast de "const int" vers "int const&"
}

(Par contre, le passage d’une "non-référence" vers une référence est implicite, donc pas de probleme pour ca).

J’espère que la question de la durée de vie des temporaires et des const est plus clair. Par contre, tu dois maintenant te poser la question de pourquoi le code que tu as donné est valide. Parce que si on suit ces explications, dans le code suivant :

int const & reference_variable { fonction () }; // #1
std::cout << reference_variable << std::endl; // #2

le temporaire retourné par la fonction "fonction" a la ligne #1 ne devrait plus être valide a la ligne #2.

La raison est qu’il existe une règle supplémentaire dont je n’ai pas encore parlé (et je ne me souviens plus si elle est expliquée dans le cours) : l’extension de durée de vie des temporaires ("C++ lifetime extension of a temporary" si tu veux faire une recherche).

En gros, cette règle dit que si un temporaire est "capturé" dans une référence, sa durée de vie est étendue a la durée de vie de la référence.

Dans ton code, la référence est valide a la ligne #2, donc le temporaire est encore valide aussi.

Pour les détails (complexes) de cette règle, cf la doc : https://en.cppreference.com/w/cpp/language/reference_initialization#Lifetime_of_a_temporary

Ou bien est-ce que cela aura le meme effet qu’une copie, comme si on faisait

int variable { fonction () };

L’effet "visible" sera la même, mais les mécanismes sont très différents. Dans ce code, le temporaire est bien détruit a la fin de l’évaluation de l’expression, il n’y a pas extension de sa durée de vie. C’est un nouvel objet, non temporaire, qui est créé par copie du temporaire. Et ce nouvel objet a une durée de vie qui n’est pas limitée a l’expression.


Donc pour résumer, 3 notions importantes :

  • le fonctionnement de la durée de vie des temporaires (limité la l’expression, par défaut)
  • le respect des const (impossible de passer implicitement d’un const vers un non const)
  • l’extension de la durée de vie des temporaires avec les références

Voila pour les explications détaillées. Si tu y vois des contractions avec les explications du cours, n’hésites pas a le dire, pour corriger mes explications ou le cours.

+5 -0

Sérieusement bcp de choses sont très clair dans ma tête. Je vous ai suivi du debut a la fin de vos explications, je ne revois aucune contradiction… Mais je relirai cependant cette partie du cour et ainsi que vos explication pour bien le digérer et l’assimiler.

Bref merci a vous pour cette réponse détaillée… Bonne année.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte