Reference constante sur donnee membre d'un objet temporaire ?
13 réponses
Aurélien REGAT-BARREL
Bonjour,
D'après ce que je sais, si une référence constante est initialisée avec un
objet temporaire, on a la garantie que l'objet temporaire persiste tant que
la référence existe. OK.
Question : et si c'est une donnée membre d'un objet temporaire :
#include <vector>
#include <iostream>
class Test
{
public:
Test()
{
vect.push_back( 1 );
vect.push_back( 2 );
vect.push_back( 3 );
}
Bonjour, D'après ce que je sais, si une référence constante est initialisée avec un objet temporaire, on a la garantie que l'objet temporaire persiste tant que la référence existe. OK. J'aimerais bien savoir d'ou tu tiens ça.
Du Stroustrup (page 110 tout en haut). De Andrei Alexandrescu dans son article sur les ScopeGuard http://www.cuj.com/documents/s00/cujcexp1812alexandr/alexandr.htm "According to the C++ Standard, a reference initialized with a temporary value makes that temporary value live for the lifetime of the reference itself."
Ce genre de pratique est à bannir. D'une manière générale il est fortement déconseillé de retourner une référence ou un pointeur sur une donnée membre (ce qui est fait dans get_vector) car on a plus aucune visibilité sur la durée de vie de l'objet retourné.
AMHA, c'est plutot le problème du mélange renvoi par référence / copie qui est à éviter. A savoir c'est get_test() et son retour par copie qui pose problème ici. A la limite, j'irai même jusqu'à dire que c'est le retour par copie qui est à bannir, car on a encore moins de visibilité sur la durée de vie de la copie temporaire crée. Renvoyer une référence, c'est quand même super courant dans les accesseurs. Surtout pour des vecteur pour qui une recopie n'est pas négligeable.
-- Aurélien REGAT-BARREL
Ultimataupe wrote:
Bonjour,
D'après ce que je sais, si une référence constante est initialisée
avec un objet temporaire, on a la garantie que l'objet temporaire
persiste tant que la référence existe. OK.
J'aimerais bien savoir d'ou tu tiens ça.
Du Stroustrup (page 110 tout en haut).
De Andrei Alexandrescu dans son article sur les ScopeGuard
http://www.cuj.com/documents/s00/cujcexp1812alexandr/alexandr.htm
"According to the C++ Standard, a reference initialized with a temporary
value makes that temporary value live for the lifetime of the reference
itself."
Ce genre de pratique est à bannir.
D'une manière générale il est fortement déconseillé de retourner une
référence ou un pointeur sur une donnée membre (ce qui est fait dans
get_vector) car on a plus aucune visibilité sur la durée de vie de
l'objet retourné.
AMHA, c'est plutot le problème du mélange renvoi par référence / copie qui
est à éviter. A savoir c'est get_test() et son retour par copie qui pose
problème ici. A la limite, j'irai même jusqu'à dire que c'est le retour par
copie qui est à bannir, car on a encore moins de visibilité sur la durée de
vie de la copie temporaire crée.
Renvoyer une référence, c'est quand même super courant dans les accesseurs.
Surtout pour des vecteur pour qui une recopie n'est pas négligeable.
Bonjour, D'après ce que je sais, si une référence constante est initialisée avec un objet temporaire, on a la garantie que l'objet temporaire persiste tant que la référence existe. OK. J'aimerais bien savoir d'ou tu tiens ça.
Du Stroustrup (page 110 tout en haut). De Andrei Alexandrescu dans son article sur les ScopeGuard http://www.cuj.com/documents/s00/cujcexp1812alexandr/alexandr.htm "According to the C++ Standard, a reference initialized with a temporary value makes that temporary value live for the lifetime of the reference itself."
Ce genre de pratique est à bannir. D'une manière générale il est fortement déconseillé de retourner une référence ou un pointeur sur une donnée membre (ce qui est fait dans get_vector) car on a plus aucune visibilité sur la durée de vie de l'objet retourné.
AMHA, c'est plutot le problème du mélange renvoi par référence / copie qui est à éviter. A savoir c'est get_test() et son retour par copie qui pose problème ici. A la limite, j'irai même jusqu'à dire que c'est le retour par copie qui est à bannir, car on a encore moins de visibilité sur la durée de vie de la copie temporaire crée. Renvoyer une référence, c'est quand même super courant dans les accesseurs. Surtout pour des vecteur pour qui une recopie n'est pas négligeable.
-- Aurélien REGAT-BARREL
Aurélien REGAT-BARREL
Est ce que le code suivant est valide dans ce cas :
int main() { const std::string & bar = foo( "bar" ); std::cout << bar; }
parce que le temporaire n'est valide que pendant l'exécution de foo().
-- Aurélien REGAT-BARREL
kanze
Ultimataupe wrote:
D'après ce que je sais, si une référence constante est initialisée avec un objet temporaire, on a la garantie que l'objet temporaire persiste tant que la référence existe. OK.
J'aimerais bien savoir d'ou tu tiens ça.
Peut-être des paragraphes 4 et 5 de la section 12.2 de la norme. Mais ce n'est rien de nouveau ; c'était déjà dans l'ARM, il me semble. (L'ARM ne spécifiait pas la durée de vie des temporaires dans le cas général, mais il me semble qu'il précisait bien que dans ce cas-ci, ils avaient au moins la durée de vie de la référence. Mais je n'ai pas ma copie sous la main pour vérifier.)
Question : et si c'est une donnée membre d'un objet temporaire :
#include <vector> #include <iostream>
class Test { public: Test() { vect.push_back( 1 ); vect.push_back( 2 ); vect.push_back( 3 ); }
int main() { const std::vector<int> & v = get_test().get_vector(); std::cout << v.size() << 'n'; }
g++ m'affiche 3, VC++ 0. Qui a raison ?
En rajoutant le destructeur de Test ~Test() { std::cout <<"~Test()" << 'n'; }
tu t'apercois que ton objet Test ( et donc le vector) est detruit avant d'appeller v.size();
Il faudrait qu'il instrumente v.size() aussi pour en être sûr. (En fait, dans un cas comme ceci, je remplacerai bien le std::vector avec une classe triviale à moi, justement afin pouvoir l'instrumenter.)
Donc v.size() est appellé sur un objet détruit.
Comme l'objet est stocké dans la pile, la zone mémoire n'est pas écrasé (sauf si tu sors de la fonction et que tu rentre dans un autre) c'est pur ça que l'appel à size() ne plante pas.
Apparemment le destructeur de vector de VC++ efface bien le tableau et remet sa taille à 0 ce qui n'est pas fait par g++. On pourrait dire que VC++ est plus propre.
Je dirais plutôt que c'est un aléa de l'implémentation. Je ne vois pas l'intérêt en général de mettre à zéro la mémoire qui cesse d'exister (en ce qui concerne le programme).
Personne n'a raison ni tord . Rien n'indique dans les spec C++ ce qu'on doit mettre dans de la mémoire non utilisée.
Ce genre de pratique est à bannir.
D'une manière générale il est fortement déconseillé de retourner une référence ou un pointeur sur une donnée membre (ce qui est fait dans get_vector) car on a plus aucune visibilité sur la durée de vie de l'objet retourné.
Tu veux dire comme ce qui est fait dans vector<>::operator[] ?
Ça peut poser des problèmes. Surtout dans les cas comme vector, où la référence peut être invalidée sans que le vector cesse d'exister. Il faut bien reconnaître le problème, et peser les alternatifs. Un vector où la seule façon de modifier les éléments, c'est une fonction put(), n'est pas l'idéal non plus. Un vector qui utilise un proxy pour le retour de l'opérateur [] (et les iterateurs) a des avantages certains, mais un coût en temps d'execution qui pourrait être genant dans certaines applications.
L'importance, c'est de faire la décision en connaissance de cause. Dans la mésure du possible, il est préférable d'éviter une durée de vie inférieur à la durée de vie de l'objet principal. Mais réalistiquement, ce n'est pas toujours possible, et dans ces cas-là, on fait ce qu'on peut, et on documente exactement ce qu'on garantit.
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Ultimataupe wrote:
D'après ce que je sais, si une référence constante est
initialisée avec un objet temporaire, on a la garantie que
l'objet temporaire persiste tant que la référence
existe. OK.
J'aimerais bien savoir d'ou tu tiens ça.
Peut-être des paragraphes 4 et 5 de la section 12.2 de la
norme. Mais ce n'est rien de nouveau ; c'était déjà dans l'ARM,
il me semble. (L'ARM ne spécifiait pas la durée de vie des
temporaires dans le cas général, mais il me semble qu'il
précisait bien que dans ce cas-ci, ils avaient au moins la durée
de vie de la référence. Mais je n'ai pas ma copie sous la main
pour vérifier.)
Question : et si c'est une donnée membre d'un objet temporaire :
#include <vector>
#include <iostream>
class Test
{
public:
Test()
{
vect.push_back( 1 );
vect.push_back( 2 );
vect.push_back( 3 );
}
int main()
{
const std::vector<int> & v = get_test().get_vector();
std::cout << v.size() << 'n';
}
g++ m'affiche 3, VC++ 0. Qui a raison ?
En rajoutant le destructeur de Test
~Test()
{
std::cout <<"~Test()" << 'n';
}
tu t'apercois que ton objet Test ( et donc le vector) est
detruit avant d'appeller v.size();
Il faudrait qu'il instrumente v.size() aussi pour en être
sûr. (En fait, dans un cas comme ceci, je remplacerai bien le
std::vector avec une classe triviale à moi, justement afin
pouvoir l'instrumenter.)
Donc v.size() est appellé sur un objet détruit.
Comme l'objet est stocké dans la pile, la zone mémoire n'est
pas écrasé (sauf si tu sors de la fonction et que tu rentre
dans un autre) c'est pur ça que l'appel à size() ne plante
pas.
Apparemment le destructeur de vector de VC++ efface bien le
tableau et remet sa taille à 0 ce qui n'est pas fait par g++.
On pourrait dire que VC++ est plus propre.
Je dirais plutôt que c'est un aléa de l'implémentation. Je ne
vois pas l'intérêt en général de mettre à zéro la mémoire qui
cesse d'exister (en ce qui concerne le programme).
Personne n'a raison ni tord . Rien n'indique dans les spec C++
ce qu'on doit mettre dans de la mémoire non utilisée.
Ce genre de pratique est à bannir.
D'une manière générale il est fortement déconseillé de
retourner une référence ou un pointeur sur une donnée membre
(ce qui est fait dans get_vector) car on a plus aucune
visibilité sur la durée de vie de l'objet retourné.
Tu veux dire comme ce qui est fait dans vector<>::operator[] ?
Ça peut poser des problèmes. Surtout dans les cas comme vector,
où la référence peut être invalidée sans que le vector cesse
d'exister. Il faut bien reconnaître le problème, et peser les
alternatifs. Un vector où la seule façon de modifier les
éléments, c'est une fonction put(), n'est pas l'idéal non plus.
Un vector qui utilise un proxy pour le retour de l'opérateur []
(et les iterateurs) a des avantages certains, mais un coût en
temps d'execution qui pourrait être genant dans certaines
applications.
L'importance, c'est de faire la décision en connaissance de
cause. Dans la mésure du possible, il est préférable d'éviter
une durée de vie inférieur à la durée de vie de l'objet
principal. Mais réalistiquement, ce n'est pas toujours possible,
et dans ces cas-là, on fait ce qu'on peut, et on documente
exactement ce qu'on garantit.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
D'après ce que je sais, si une référence constante est initialisée avec un objet temporaire, on a la garantie que l'objet temporaire persiste tant que la référence existe. OK.
J'aimerais bien savoir d'ou tu tiens ça.
Peut-être des paragraphes 4 et 5 de la section 12.2 de la norme. Mais ce n'est rien de nouveau ; c'était déjà dans l'ARM, il me semble. (L'ARM ne spécifiait pas la durée de vie des temporaires dans le cas général, mais il me semble qu'il précisait bien que dans ce cas-ci, ils avaient au moins la durée de vie de la référence. Mais je n'ai pas ma copie sous la main pour vérifier.)
Question : et si c'est une donnée membre d'un objet temporaire :
#include <vector> #include <iostream>
class Test { public: Test() { vect.push_back( 1 ); vect.push_back( 2 ); vect.push_back( 3 ); }
int main() { const std::vector<int> & v = get_test().get_vector(); std::cout << v.size() << 'n'; }
g++ m'affiche 3, VC++ 0. Qui a raison ?
En rajoutant le destructeur de Test ~Test() { std::cout <<"~Test()" << 'n'; }
tu t'apercois que ton objet Test ( et donc le vector) est detruit avant d'appeller v.size();
Il faudrait qu'il instrumente v.size() aussi pour en être sûr. (En fait, dans un cas comme ceci, je remplacerai bien le std::vector avec une classe triviale à moi, justement afin pouvoir l'instrumenter.)
Donc v.size() est appellé sur un objet détruit.
Comme l'objet est stocké dans la pile, la zone mémoire n'est pas écrasé (sauf si tu sors de la fonction et que tu rentre dans un autre) c'est pur ça que l'appel à size() ne plante pas.
Apparemment le destructeur de vector de VC++ efface bien le tableau et remet sa taille à 0 ce qui n'est pas fait par g++. On pourrait dire que VC++ est plus propre.
Je dirais plutôt que c'est un aléa de l'implémentation. Je ne vois pas l'intérêt en général de mettre à zéro la mémoire qui cesse d'exister (en ce qui concerne le programme).
Personne n'a raison ni tord . Rien n'indique dans les spec C++ ce qu'on doit mettre dans de la mémoire non utilisée.
Ce genre de pratique est à bannir.
D'une manière générale il est fortement déconseillé de retourner une référence ou un pointeur sur une donnée membre (ce qui est fait dans get_vector) car on a plus aucune visibilité sur la durée de vie de l'objet retourné.
Tu veux dire comme ce qui est fait dans vector<>::operator[] ?
Ça peut poser des problèmes. Surtout dans les cas comme vector, où la référence peut être invalidée sans que le vector cesse d'exister. Il faut bien reconnaître le problème, et peser les alternatifs. Un vector où la seule façon de modifier les éléments, c'est une fonction put(), n'est pas l'idéal non plus. Un vector qui utilise un proxy pour le retour de l'opérateur [] (et les iterateurs) a des avantages certains, mais un coût en temps d'execution qui pourrait être genant dans certaines applications.
L'importance, c'est de faire la décision en connaissance de cause. Dans la mésure du possible, il est préférable d'éviter une durée de vie inférieur à la durée de vie de l'objet principal. Mais réalistiquement, ce n'est pas toujours possible, et dans ces cas-là, on fait ce qu'on peut, et on documente exactement ce qu'on garantit.
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34