OVH Cloud OVH Cloud

Reference constante et temporaire...

5 réponses
Avatar
Jean-Marc Bourguet
On peut lier une reference constante a un temporaire. La duree de vie
du temporaire est alors etendue a celle de la constante. Mais qu'en
est-il si on prent un sous-objet d'un temporaire? Autrement dit,
quelle est la sortie attendue pour:

#include <iostream>

struct S1 {
S1()
{ std::cout << "S1::S1()\n"; }
S1(S1 const&)
{ std::cout << "S1::S1(S1 const&)\n"; }
~S1()
{ std::cout << "S1::~S1()\n"; }
};

struct S2 {
S2()
{ std::cout << "S2::S2()\n"; }
S2(S2 const&)
{ std::cout << "S2::S2(S2 const&)\n"; }
~S2()
{ std::cout << "S2::~S2()\n"; }

int i;
S1 s1;
};

S2 f() {
return S2();
}

int main() {
{
S1 const& s1 = f().s1;
std::cout << "INSIDE\n";
}
}

Sur 5 compilateurs, j'ai quatre resultats differents.
Sun C++ 5.5 Patch 113817-06 2004/01/29
S1::S1()
S2::S2()
INSIDE
S2::~S2()
S1::~S1()

gcc 3.2.2 (clairement un bug vu le comportement de 3.4.2)
S1::S1()
S2::S2()
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()

gcc 3.4.2
S1::S1()
S2::S2()
S1::S1(S1 const&)
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()

aCC: HP ANSI C++ B3910B A.03.52
S1::S1()
S2::S2()
INSIDE
S2::~S2()
S1::~S1()

VisualAge C++ Professional / C for AIX Compiler, Version 6
S1::S1()
S2::S2()
S2::~S2()
S1::~S1()
INSIDE

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

5 réponses

Avatar
Patrick Mézard
Jean-Marc Bourguet wrote:
On peut lier une reference constante a un temporaire. La duree de vie
du temporaire est alors etendue a celle de la constante. Mais qu'en
est-il si on prent un sous-objet d'un temporaire? Autrement dit,
quelle est la sortie attendue pour:

#include <iostream>

struct S1 {
S1()
{ std::cout << "S1::S1()n"; }
S1(S1 const&)
{ std::cout << "S1::S1(S1 const&)n"; }
~S1()
{ std::cout << "S1::~S1()n"; }
};

struct S2 {
S2()
{ std::cout << "S2::S2()n"; }
S2(S2 const&)
{ std::cout << "S2::S2(S2 const&)n"; }
~S2()
{ std::cout << "S2::~S2()n"; }

int i;
S1 s1;
};

S2 f() {
return S2();
}

int main() {
{
S1 const& s1 = f().s1;
std::cout << "INSIDEn";
}
}


Je ne pense pas avoir plus de réponse que toi, mais en grattant un peu
dans google.groups on trouve des trucs intéressants :

<http://groups.google.fr/groups?hl=fr&lr=&selm>0DA209.8060004%40online.fr>

---------------------
It is quite a long paragraph, but it says:

12.2/5

The second context is when a reference is bound to a temporary. The
temporary to which the reference is bound or the temporary that is the
complete object to a subobject of which the temporary is bound persists
for the lifetime of the reference as specified below. A temporary bound
to a reference member in a constructor's c-tor initializer(12.6.2)
persists until the constructor exits. A temporary bound to a reference
parameter in a function call (5.2.2) persists until the completion of
the full expression containing the call. A temporary bound to the
returned value in a function return statement (6.6.3) persists until the
function exits. In all these cases, the temporaries created during the
evaluation of the expression initializing the reference, except the
temporary to which the reference is bound, are destroyed at the end of
the full-expression in which they are created and in the reverse order
of the completion of their construction. In addition, the destruction of
temporaries bound to references shall take into account the ordering of
destruction of objects with static or automatic storage duration (3.7.1,
3.7.2); that is, if obj1 is an object with static or automatic storage
duration created before the temporary is created, the temporary shall be
destroyed before obj1 is destroyed; if obj2 is an object with static or
automatic storage duration created after the temporary is created, the
temporary shall be destroyed after obj2 is destroyed.

--End of standard citation
---------------------

Et il y a aussi ce fil là assez proche de ce que tu fais :
<http://groups.google.fr/groups?hl=fr&lr=&selm=uiu0Tdi%24EHA.2700%40TK2MSFTNGP14.phx.gbl&rnum>

---------------------
The issue is that inside the GetData method, "*this" is an lvalue, not a
temporary, so member variables are also lvalues. As a result, the rules
for binding an lvalue to a reference apply. Direct binding to a
subobject of a temporary is not happening. Here's an example where you
might have direct binding to a subobject:

#include <iostream>
using namespace std;

struct B
{
B(){cout << "B()n";}
B(B const&){cout << "B(B const&)n";}
~B(){cout << "~B()n";}
};

struct A
{
A(){cout << "A()n";}
A(A const&){cout << "A(const&)n";}
~A(){cout << "~A()n";}
B b;
};

int main()
{
B const& b = A().b; //possible binding to a subobject of a temporary
cout << "end of scopen";
}

As you can see, b is bound to A().b, an rvalue. Unfortunately even in
this example you have no guarantee that the A() object will persist,
since b may bind to a copy of A().b (see 8.5.3/5) (in which case the
second temporary B object will persist but the first won't). On my
compilers, Comeau C++ extends the lifetime of the whole temporary A
object till the end of main. OTOH, GCC 3.4 instead binds b to a copy of
A().b, and thus destroys the A temporary (and original B subobject)
before the end of main.

Tom
---------------------


Donc, ou bien le compilo conserve tout l'objet englobant ou bien il
génère une copie du sous-objet et lie la référence avec. Maintenant, il
a l'air assez libre dans ses choix.

Sur 5 compilateurs, j'ai quatre resultats differents.
Sun C++ 5.5 Patch 113817-06 2004/01/29
S1::S1()
S2::S2()
INSIDE
S2::~S2()
S1::~S1()

gcc 3.2.2 (clairement un bug vu le comportement de 3.4.2)
S1::S1()
S2::S2()
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()

gcc 3.4.2
S1::S1()
S2::S2()
S1::S1(S1 const&)
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()

aCC: HP ANSI C++ B3910B A.03.52
S1::S1()
S2::S2()
INSIDE
S2::~S2()
S1::~S1()

VisualAge C++ Professional / C for AIX Compiler, Version 6
S1::S1()
S2::S2()
S2::~S2()
S1::~S1()
INSIDE


Microsoft Visual C++ .NET (7.1)
S1::S1()
S2::S2()
S1::S1(S1 const&)
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()

Après tout ça, j'ai tendance à dire que 1, 3, 4, 6 sont dans le vrai.
Visual Age (5), détruit peut-être les temporaires un peu vite, je ne
sais pas, peut-être qu'il a le droit. Quant à gcc 3.2.2 il est
clairement buggé.

Y a plus qu'à attendre l'avis des experts du standard...

Patrick Mézard

Avatar
Alex
Jean-Marc Bourguet wrote:
gcc 3.2.2 (clairement un bug vu le comportement de 3.4.2)
S1::S1()
S2::S2()
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()

gcc 3.4.2
S1::S1()
S2::S2()
S1::S1(S1 const&)
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()


Logiquement, le 3.4.2 a tout à fait raison !

Si on n'a besoin que de la s1, inutile de garder tout un objet s2, qui peut
être beaucoup plus lourd qu'un simple "int", peux avoir la connexion à BDD,
fichiers ouverts etc...

Les autres compilateurs abusent et le dernier rend la variable s1 non
utilisable... mais j'écrirais jamais un truc pareil pour plus de sérénité
surtout pour ceux qui viennent lire et débugger mon code après moi

Avatar
Olivier Azeau
Alex wrote:
Jean-Marc Bourguet wrote:

gcc 3.2.2 (clairement un bug vu le comportement de 3.4.2)
S1::S1()
S2::S2()
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()

gcc 3.4.2
S1::S1()
S2::S2()
S1::S1(S1 const&)
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()



Logiquement, le 3.4.2 a tout à fait raison !

Si on n'a besoin que de la s1, inutile de garder tout un objet s2, qui peut
être beaucoup plus lourd qu'un simple "int", peux avoir la connexion à BDD,
fichiers ouverts etc...


Mais ça veut dire quoi "garder s1 sans garder s2" quand s1 est une
donnée membre de s2 ???
Et si s1 a besoin de la connexion à la BDD pour fonctionner ?

Pour moi les compilos qui font une copie de s1 alors que l'on n'en veut
qu'une référence ne font rien de plus que mettre le développeur en
difficulté...

Les autres compilateurs abusent et le dernier rend la variable s1 non
utilisable... mais j'écrirais jamais un truc pareil pour plus de sérénité
surtout pour ceux qui viennent lire et débugger mon code après moi


Moi non plus je n'écrirai jamais ça dans une vraie appli pour les mêmes
raisons. Les objets temporaires gérés par le compilo c'est un très bon
moyen de production de bugs...


Avatar
Gabriel Dos Reis
Jean-Marc Bourguet writes:

| On peut lier une reference constante a un temporaire. La duree de vie
| du temporaire est alors etendue a celle de la constante. Mais qu'en
| est-il si on prent un sous-objet d'un temporaire? Autrement dit,
| quelle est la sortie attendue pour:
|
| #include <iostream>
|
| struct S1 {
| S1()
| { std::cout << "S1::S1()n"; }
| S1(S1 const&)
| { std::cout << "S1::S1(S1 const&)n"; }
| ~S1()
| { std::cout << "S1::~S1()n"; }
| };
|
| struct S2 {
| S2()
| { std::cout << "S2::S2()n"; }
| S2(S2 const&)
| { std::cout << "S2::S2(S2 const&)n"; }
| ~S2()
| { std::cout << "S2::~S2()n"; }
|
| int i;
| S1 s1;
| };
|
| S2 f() {
| return S2();
| }
|
| int main() {
| {
| S1 const& s1 = f().s1;

Faire une copie de f().s1 et lier la lier å s1..

[...]

| gcc 3.2.2 (clairement un bug vu le comportement de 3.4.2)

oui, et cela a été corrigé récemment.

-- Gaby
Avatar
kanze
Jean-Marc Bourguet wrote:
On peut lier une reference constante a un temporaire. La duree
de vie du temporaire est alors etendue a celle de la
constante. Mais qu'en est-il si on prent un sous-objet d'un
temporaire?


Ça dépend quel genre de sous-objet. Une base se comporte
différemment à un membre.

Autrement dit, quelle est la sortie attendue pour:

#include <iostream>

struct S1 {
S1()
{ std::cout << "S1::S1()n"; }
S1(S1 const&)
{ std::cout << "S1::S1(S1 const&)n"; }
~S1()
{ std::cout << "S1::~S1()n"; }
};

struct S2 {
S2()
{ std::cout << "S2::S2()n"; }
S2(S2 const&)
{ std::cout << "S2::S2(S2 const&)n"; }
~S2()
{ std::cout << "S2::~S2()n"; }

int i;
S1 s1;
};

S2 f() {
return S2();
}

int main() {
{
S1 const& s1 = f().s1;
std::cout << "INSIDEn";
}
}

Sur 5 compilateurs, j'ai quatre resultats differents.
Sun C++ 5.5 Patch 113817-06 2004/01/29
S1::S1()
S2::S2()
INSIDE
S2::~S2()
S1::~S1()


Légal. Selon §12.2/5, « The temporary to which the reference is
bound, OR the temporary that is the complete object to a
subobject of which the temporary is bound persists for the
lifetime of the reference [...]. »

Sun implémente la deuxième proposition de l'OR.

gcc 3.2.2 (clairement un bug vu le comportement de 3.4.2)
S1::S1()
S2::S2()
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()


Clairement un bug, quand on construit deux objets, mais en
detruit trois. Ça me semble une bonne recette pour un core dump,
pour peu que S1 contient des membres non triviaux.

gcc 3.4.2
S1::S1()
S2::S2()
S1::S1(S1 const&)
S2::~S2()
S1::~S1()
INSIDE
S1::~S1()


Ça me semble correct aussi. G++ a choisi la première
proposition dans la phrase ci-dessus, et a profité de la liberté
de faire une copie « gratuite » donnée dans §8.5.3/5 (deuxième
point dans le traitement des expressions rvalue).

Je mets « gratuite » en guillemets, parce que dans ce cas
précis, il y a bien une justification pour la copie (et donc,
pour l'obligation que le temporaire ait un constructeur de copie
accessible). Je n'aime pas trop l'interprétation de g++, mais
c'est clairement légal, et on pourrait même arguer qu'elle
représente mieux les intentions des auteurs de la norme que
l'interprétation de Sun et de HP.

aCC: HP ANSI C++ B3910B A.03.52
S1::S1()
S2::S2()
INSIDE
S2::~S2()
S1::~S1()


Comme pour Sun CC.

VisualAge C++ Professional / C for AIX Compiler, Version 6
S1::S1()
S2::S2()
S2::~S2()
S1::~S1()
INSIDE


Clairement erroné aussi. Selon §12.2/5, je crois que c'est
clair qu'il doit y avoir un objet de type S1 qui vit autant que
la référence.

En fait, je crois que l'implémentation a plusieurs choix ici. Si
je ne régarde que §8.5.3, je dirais que g++ a raison, mais la
deuxième phrase de §12.2/5 semble ajouter la liberté nécessaire
pour que Sun et HP soit correct aussi.

Côté utilisabilité, je préfère l'interprétation de Sun et de HP,
de loin. Mais les utilisations n'en sont possibles que si
garantie par la norme, ce qui n'est clairement pas le cas.

--
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