référence vers pointeur

Le
Marc Guéguen
Bonjour,
je viens de changer de version de mon compilateur et il n'accepte plus la
fonction suivante :

CObject*& getter_label(CObject* owner) {
return
static_cast<CObject*>(static_cast<CObjectFieldBase*>(owner)->label_);
}

Les cast sont corrects. Il me met le message :
[BCC32 Erreur] CObjectField.cpp(366): E2357 Référence initialisée avec
'CObject *', nécessite lvalue de type 'CObject *'
Ma question : je ne vois pas où est l'erreur
J'initialise bien la référence avec un pointeur du même type.
Merci pour votre aide.
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Marc Guéguen
Le #23563941
Je viens de me rendre compte que ça marche en changeant static_cast en
reinterpret_cast, comme suit :

CObject*& getter_label(CObject* owner) {
return
reinterpret_cast<CObject*>(static_cast<CObjectFieldBase*>(owner)->label_);
}

Je n'aime pas trop les reinterpret. Ma question demeure...
Fabien LE LEZ
Le #23565061
On Thu, 14 Jul 2011 17:05:22 +0200, Marc Guéguen

CObject*& getter_label(CObject* owner) {
return
static_cast<CObject*>(static_cast<CObjectFieldBase*>(owner)->label_);
}



Quelle est la relation entre CObject et CObjectFieldBase ? Est-ce que
l'un hérite de l'autre ?
Idem pour le type de "label_".
Fabien LE LEZ
Le #23565051
On Thu, 14 Jul 2011 17:05:22 +0200, Marc Guéguen

CObject*& getter_label(CObject* owner) {
return
static_cast<CObject*>(static_cast<CObjectFieldBase*>(owner)->label_);



Je vois au moins une couille dans ce code.
Ton code est équivalent à :

CObject*& getter_label(CObject* owner)
{
CObject* temp= static_cast<CObject*>(...
return temp;
}

Tu crées un objet temporaire (de type "pointeur vers CObject"), qui va
être détruit à la fin de la fonction.
Donc, tu ne peux pas renvoyer une référence vers ce pointeur,
puisqu'il n'existe plus.

Je ne sais pas bien pourquoi ton précédent compilateur acceptait ça --
à moins que quelque chose ne m'échappe.

La solution : renvoyer le pointeur, pas une référence vers ce
pointeur.

CObject* getter_label(CObject* owner)
{ ...


[BCC32 Erreur]



Borland ? Il a quel âge, ce compilo ?
Marc Guéguen
Le #23566001
Mon compilateur - sur lequel ce code compile - est BCB 2010, donc pas trop
vieux.
CObjectFieldBase dérive de CObject, de même que
static_cast<CObjectFieldBase*>(owner)->label_
et tous les cast sont donc valides.

Je vois au moins une couille dans ce code.
Ton code est équivalent à :

CObject*& getter_label(CObject* owner)
{
CObject* temp= static_cast<CObject*>(...
return temp;
}

Tu crées un objet temporaire (de type "pointeur vers CObject"), qui va
être détruit à la fin de la fonction.
Donc, tu ne peux pas renvoyer une référence vers ce pointeur,
puisqu'il n'existe plus.



C'est ce que je constate et justement, je ne comprends pas pourquoi un
temporaire est créé !
Pour moi, un static_cast, c'est simplement un cast effectué à la compilation
dans une hiérarchie.
Je ne vois pas pourquoi un temporaire serait créé.

Déjà, mon compilateur - BCB 2010 - n'accepte pas la syntaxe
CObject*& getter_label(CObject* owner) {
return static_cast<CObjectFieldBase*>(owner)->label_;
}
qui correspond à l'initialisation de la référence à partir d'un pointeur de
classe dérivée et m'oblige à un static_cast supplémentaire.

La solution : renvoyer le pointeur, pas une référence vers ce
pointeur.

CObject* getter_label(CObject* owner)
{ ...



J'ai besoin d'une référence vers le pointeur d'origine, car je veux à partir
du retour de fonction pouvoir changer l'objet pointé "à l'emplacement
désigné", ce qui n'est pas possible si je retourne un simple pointeur.
En gros, tu l'as sans doute compris, label est une propriété d'objet
implémentée sous la forme d'un pointeur alloué dans le tas. Ce getter me
retourne la propriété que je dois pouvoir modifier (et pas seulement lire).
Si je retourne un CObject*, j'aurais une simple copie du pointeur...

Si static_cast effectue une copie, il va falloir que je mette des
reinterpret_cast partout !
(et j'espère que reinterpret_cast ne fait pas de copie, enfin je ne pense
pas !)
Mais c'est vraiment une surprise pour moi, et je trouve ce comportement pas
très pratique...
Fabien LE LEZ
Le #23567921
On Fri, 15 Jul 2011 07:48:12 +0200, Marc Guéguen

BCB 2010, donc pas trop vieux.



C++ Builder ? Je le croyais mort depuis des années, mais apparemment
il survit. Étonnant.


CObjectFieldBase dérive de CObject



En gros, ça ressemble à :

class CObject
{
...
};

class Label: public CObject
{
public: int a; // J'en aurai besoin plus tard.
...
};

class CObjectFieldBase: public CObject
{
public: Label* label_;
...
};

C'est bien ça ?


CObject*& getter_label(CObject* owner) {
... static_cast<CObjectFieldBase*>(owner)



Puisque CObjectFieldBase dérive de CObject, il faudrait plutôt un
dynamic_cast<> ici, non ?


J'ai besoin d'une référence vers le pointeur d'origine, car je veux à partir
du retour de fonction pouvoir changer l'objet pointé "à l'emplacement
désigné



C'est très casse-gueule.

En fait, tu as une fonction "get" qui te sert à faire un "set".
Si tu peux modifier le code client, il vaut bien mieux créer une
fonction "set_..." qui te permet, comme son nom l'indique, de modifier
un truc.

Mébon, voyons ce qu'on peut faire avec cette fonction :

CObject*& getter_label (CObject* owner);

void code_ok()
{
CObjectFieldBase x;

Label l;
getter_label (&x)= &l;
// OK, x.label_ pointe bien vers un "Label".

std::cout }

void code_moins_ok()
{
CObjectFieldBase x;

CObject un_truc;
getter_label (&x)= &un_truc;
// Tout à coup, x.label_ pointe vers un objet de type "CObject".

std::cout /* Ici, on essaie d'afficher le membre "a" d'un objet qui, en fait,
est de type "CObject", et donc n'a pas de membre "a". */
}

En gros, le système de types de C++ ne fonctionne plus. Ce qui
signifie que tu l'as cassé. Et comme la seule façon de casser le
système de types de C++ est d'utiliser reinterpret_cast<>, tu as
forcément un reinterpret_cast<> dans ta fonction getter_label().

Note que, puisque tu abandonnes le système de types, tu pourrais aussi
bien utiliser des void* :

void*& getter_label (CObject* owner);
Marc Guéguen
Le #23568441
Puisque CObjectFieldBase dérive de CObject, il faudrait plutôt un
dynamic_cast<> ici, non ?



je suis absolument sûr de la conversion => static_cast est plus performant.

En fait, tu as une fonction "get" qui te sert à faire un "set".


Il peut faire les 2 en effet.
C'est un "getter" qui peut faire setter sans contrôle de la valeur affectée.
J'ai aussi des setters, avec contrôle cette fois-ci, d'où le nom.

En gros, le système de types de C++ ne fonctionne plus. Ce qui
signifie que tu l'as cassé. Et comme la seule façon de casser le
système de types de C++ est d'utiliser reinterpret_cast<>, tu as
forcément un reinterpret_cast<> dans ta fonction getter_label().

Note que, puisque tu abandonnes le système de types, tu pourrais aussi
bien utiliser des void* :



Ce que je comprends, et qui est sans importance si on utilise pas les
références, c'est que static_cast retourne un temporaire !

Si je fais :
double d.2;
int i=static_cast<int>(d);
je m'en doute (et en pratique je l'oublie en fait)

Mais avec les pointeurs sur des objets d'une même hierarchie, c'est plus
vicieux... :

struct A {
};
struct B : public A {
int x_;
};

A *aPtr=new B;

B* b1Ptr=static_cast<B*>(aPtr);
b1Ptr->x_; // ok

B*& b2Ptr=static_cast<B*>(aPtr);
b2Ptr->x_; // problème !!

Conclusion : je dois remplacer mes static_cast par des reinterpret_cast...

CObject*& getter_label(CObject* owner) {
return
reinterpret_cast<CObject*>(reinterpret_cast<CObjectFieldBase*>(owner)->label_);
}
Fabien LE LEZ
Le #23568861
En fait, c'est surtout que dans ton code :

CObject*& getter_label(CObject* owner) {
return static_cast<CObject*>(static_cast<CObjectFieldBase*>(owner)->label_);



Tu as un cast de "CObject*" vers "CObject*&" qui n'est pas explicité.

Il faudrait écrire :

return x_cast
Or, il n'est pas possible de caster un pointeur vers un pointeur vers
un objet, en un pointeur vers un pointeur vers un objet d'une autre
classe, pour les raisons que je t'ai indiquées.

reinterpret_cast<> s'en fout ; il prend juste l'adresse mémoire de
l'élément (sous forme d'un entier assez long), et lui assigne
n'importe quel type.
Évidemment, à l'usage, le résultat est imprédictible.
Le seul truc qui peut fonctionner, c'est un reinterpret_cast<> d'un X*
vers un Y*, puis vers un X* à nouveau, en n'utilisant jamais le Y* tel
quel. Et le type le plus raisonnable pour Y est void, i.e. l'absence
de type.
James Kanze
Le #23586471
On Jul 15, 6:48 am, Marc Guéguen
[...]
> Je vois au moins une couille dans ce code.
> Ton code est équivalent à :

> CObject*& getter_label(CObject* owner)
> {
> CObject* temp= static_cast<CObject*>(...
> return temp;
> }

> Tu crées un objet temporaire (de type "pointeur vers CObject"), qui v a
> être détruit à la fin de la fonction.
> Donc, tu ne peux pas renvoyer une référence vers ce pointeur,
> puisqu'il n'existe plus.

C'est ce que je constate et justement, je ne comprends pas pourquoi un
temporaire est créé !



Le résultat d'une conversion de type est toujours un temporaire
(rvalue), au moins que la conversion donne une référence.

Pour moi, un static_cast, c'est simplement un cast effectué à
la compilation dans une hiérarchie. Je ne vois pas pourquoi
un temporaire serait créé.



Le résultat de la conversion, c'est bien un pointeur. Qui
n'existait pas avant. Où se trouverait ce pointeur, sinon dans
un temporaire ?

Déjà, mon compilateur - BCB 2010 - n'accepte pas la syntaxe
CObject*& getter_label(CObject* owner) {
return static_cast<CObjectFieldBase*>(owner)->label_;}

qui correspond à l'initialisation de la référence à partir
d'un pointeur de classe dérivée et m'oblige à un static_cast
supplémentaire.



Ce n'a rien à voir avec classe dérivée ou non. Il y a deux
problèmes avec ton code. Le premier, Fabien l'a déjà dit : tu
crées un pointeur temporaire, et tu essaies de renvoyer une
référence à ce pointeur. Ce qui donne un comportement indéfini.
(De tête, je ne me rappelle plus si le comportement indéfini
apparaît dès le « return », ou si c'est seulement quand tu
essaies d'utiliser la référence renvoyée. Je crois plutôt le
premier.)

Deuxièmement, lors du return, tu initialises une référence avec
un rvalue (un temporaire). Ce qui n'est légal que si c'est une
référence à const, ce qui n'est pas le cas ici. Et cette erreur
exige une diagnostique. (C'est une « innovation » de ne pas le
permettre, si on veut, mais l'innovation date d'environ 1990.
N'empèche que certains compilateurs continuent à l'accepter,
parfois même sans avertissement.)

Pour mieux comprendre ce qui se passe, je réécris ton original
code en donnant un nom à chaque variable :

CObject*&
getter_label( CObject* owner )
{
CObjectFieldBase* tmp1 =
static_cast<CObjectFieldBase*>( owner );
Type???* tmp2 = tmp1->label_; // Je ne sais pas le type ici.
CObject* tmp3 = static_cast<CObject*>( tmp2 );
CObject*& tmp4 = tmp3;
return tmp4;
}

D'abord, il doit être clair ici que la référence tmp4 renvoie à
une variable locale (en fait un temporaire), qui cessera
d'exister une fois on a quitté la fonction. Et deuxièmement,
qu'on initialise la référence avec tmp3. Qui est en temporaire
(rvalue) dans le code initial, et donc, l'initialisation de la
référence est illégale.

> La solution : renvoyer le pointeur, pas une référence vers ce
> pointeur.

> CObject* getter_label(CObject* owner)
> { ...

J'ai besoin d'une référence vers le pointeur d'origine, car je veux à partir
du retour de fonction pouvoir changer l'objet pointé "à l'emplacement
désigné", ce qui n'est pas possible si je retourne un simple pointeur .



Quel pointeur d'origine ?

En fait, quel est le type de CObjectFieldBase::label_ ? Si tu
veux pouvoir le modifier, il faut renvoyer une référence avec le
bon type : si ce n'est pas un CObject*, tout essai de le
modifier par une référence à un CObject* (c-à-d une lvalue de
type CObject*) est un comportement indéfini.

En gros, tu l'as sans doute compris, label est une propriété
d'objet implémentée sous la forme d'un pointeur alloué dans le
tas. Ce getter me retourne la propriété que je dois pouvoir
modifier (et pas seulement lire). Si je retourne un CObject*,
j'aurais une simple copie du pointeur...

Si static_cast effectue une copie, il va falloir que je mette
des reinterpret_cast partout ! (et j'espère que
reinterpret_cast ne fait pas de copie, enfin je ne pense pas
!) Mais c'est vraiment une surprise pour moi, et je trouve ce
comportement pas très pratique...



La solution simple, évidemment, c'est de fournir une fonction
« setter », qui prend la nouvelle valeur. Ce qui a l'avantage de
respecter (au moins un peu) l'encapsulation. Reinterpret_cast ou
static_cast ne change rien ici : le résultat d'une cast est une
rvalue, sauf si le type cible est une référence. Si tu veux
faire avec des cast et une référence de retour, il faut quelque
chose du genre :

CObject*&
getter_label( CObject* owner )
{
return reinterpret_cast<CObject*&>(
static_cast<CObjectFieldBase*>( owner )->label_ );
}

Seulement, c'est un comportement indéfini. Qui marcherait dans
la pratique *SI* il n'y a que l'héritage simple (sans héritage
multiple ni virtuel), et que le compilateur n'est pas g++ (et
peut-être d'autres, mais ça marchera avec VC++).

--
James Kanze
Marc Guéguen
Le #23608971
merci pour ta réponse.
J'ai finalement implémenté plus distinctement getter et setter :
ça me fait du code plus lourd, mais bon, je suis plus certain du résultat !
Désolé pour ma réponse tardive, période estivale oblige...
Publicité
Poster une réponse
Anonyme