Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Polymorphisme, sémantique de valeur, durée de vie des temporaires...

38 réponses
Avatar
Marc Boyer
Bonjour,

j'ai un problème 'pure virtual method called' (avec g++), et je n'arrive
pas à faire d'exemple minimal qui reproduise le pb...

Mais j'ai peut etre identifié le pb. Quand j'écris un truc du genre
X x( Y(1) );
avec X et Y deux classes, quelle est la durée de vie de l'objet
temporaire Y ?

Actuellement, je prends une référence sur cet objet, et visiblement
il aime pas. Alors que si j'écris
X x( *(new Y(1)) );
ça marche bien.

Merci,

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

10 réponses

1 2 3 4
Avatar
Jean-Marc Bourguet
Marc Boyer writes:

j'ai un problème 'pure virtual method called' (avec g++), et je n'arrive
pas à faire d'exemple minimal qui reproduise le pb...


Generalement ca arrive quand on appelle un membre virtuel dans le
constructeur ou le destructeur d'un objet.

Mais j'ai peut etre identifié le pb. Quand j'écris un truc du genre
X x( Y(1) );
avec X et Y deux classes, quelle est la durée de vie de l'objet
temporaire Y ?


Comme tous les temporaires qui ne sont pas bindé a une variable reference:
jusqu'a la fin de l'expression.

Actuellement, je prends une référence sur cet objet,


Si tu la conserve au dela du constructeur de x (comme le laisse croire la
suite), c'est fort possible que ce soit la source de ton probleme (et on ne
se retrouve pas dans le cas general mais vraissemblablement tu appelles le
membre virtuel apres la fin de l'execution du destructeur de Y(1)).

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

Avatar
Marc Boyer
Le 07-03-2007, Jean-Marc Bourguet a écrit :
Marc Boyer writes:
j'ai un problème 'pure virtual method called' (avec g++), et je n'arrive
pas à faire d'exemple minimal qui reproduise le pb...


Generalement ca arrive quand on appelle un membre virtuel dans le
constructeur ou le destructeur d'un objet.


Là, ce doit être après la desctruction de l'objet.

Mais j'ai peut etre identifié le pb. Quand j'écris un truc du genre
X x( Y(1) );
avec X et Y deux classes, quelle est la durée de vie de l'objet
temporaire Y ?


Comme tous les temporaires qui ne sont pas bindé a une variable reference:
jusqu'a la fin de l'expression.


Oui, et ici, l'expression termine à la fin du constructeur de x( );

Actuellement, je prends une référence sur cet objet,


Si tu la conserve au dela du constructeur de x (comme le laisse croire la
suite), c'est fort possible que ce soit la source de ton probleme (et on ne
se retrouve pas dans le cas general mais vraissemblablement tu appelles le
membre virtuel apres la fin de l'execution du destructeur de Y(1)).


Oui, c'est ça. Ce qui est déstabilisant, c'est que ça fait partie
des trucs qui arrivent 'parfois'. En fait, j'avais fait un exemple
minimal à poster ici, et sur l'exemple, ça plante pas.

Merci.

#include <algorithm>
#include <cassert>

struct Fonction {
virtual double operator()(double x) const = 0;
virtual ~Fonction(){};
};

class Affine : public Fonction {
double a;
public:
Affine(double a):a(a){};
double operator()(double x) const {
return a*x;
}
};

class Min : public Fonction {
const Fonction& f;
const Fonction& g;
public:
Min(const Fonction& f, const Fonction& g):
f(f), g(g){};
double operator()(double x) const {
return std::min( f(x), g(x) );
}
};

int main(){
Min m( Affine(2), Affine(3) );
assert( m(1)== 2 );
Min m2( Min(Affine(2), Affine(3)), Affine(4) );
assert( m(1)== 2 );
}

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)


Avatar
Laurent Deniau
Marc Boyer wrote:
Marc Boyer writes:
j'ai un problème 'pure virtual method called' (avec g++), et je n'arrive
pas à faire d'exemple minimal qui reproduise le pb...
Generalement ca arrive quand on appelle un membre virtuel dans le

constructeur ou le destructeur d'un objet.


Là, ce doit être après la desctruction de l'objet.

Mais j'ai peut etre identifié le pb. Quand j'écris un truc du genre
X x( Y(1) );
avec X et Y deux classes, quelle est la durée de vie de l'objet
temporaire Y ?
Comme tous les temporaires qui ne sont pas bindé a une variable reference:

jusqu'a la fin de l'expression.


Oui, et ici, l'expression termine à la fin du constructeur de x( );

Actuellement, je prends une référence sur cet objet,
Si tu la conserve au dela du constructeur de x (comme le laisse croire la

suite), c'est fort possible que ce soit la source de ton probleme (et on ne
se retrouve pas dans le cas general mais vraissemblablement tu appelles le
membre virtuel apres la fin de l'execution du destructeur de Y(1)).


Oui, c'est ça. Ce qui est déstabilisant, c'est que ça fait partie
des trucs qui arrivent 'parfois'. En fait, j'avais fait un exemple
minimal à poster ici, et sur l'exemple, ça plante pas.


Probablement parce que ton compilateur ne detruit les temporaires qu'a
la fin du bloc main. Possible que ce soit parce que tu declares f et g
comme des references const dont la semantique a persiste au dela du
constructeur (post-inlining: le bloc du constructeur a ete etendu au
bloc du main). Mais c'est de la cuisine interne au compilateur.
Normalement les temporaires references sans const sont detruits a la fin
de l'expression. Avec le const, la duree de vie est etendue jusqu'a la
fin du bloc.

Merci.

#include <algorithm>
#include <cassert>

struct Fonction {
virtual double operator()(double x) const = 0;
virtual ~Fonction(){};
};

class Affine : public Fonction {
double a;
public:
Affine(double a):a(a){};
double operator()(double x) const {
return a*x;
}
};

class Min : public Fonction {
const Fonction& f;
const Fonction& g;


Probleme ici. Il ne faut pas utiliser des references mais des copies.

public:
Min(const Fonction& f, const Fonction& g):
f(f), g(g){};
double operator()(double x) const {
return std::min( f(x), g(x) );
}
};

int main(){
Min m( Affine(2), Affine(3) );
assert( m(1)== 2 );
Min m2( Min(Affine(2), Affine(3)), Affine(4) );
assert( m(1)== 2 );
}


En revanche, je ne vois pas le rapport avec 'pure virtual call' dans cet
exemple.

a+, ld.



Avatar
Marc Boyer
Le 07-03-2007, Laurent Deniau a écrit :
Probablement parce que ton compilateur ne detruit les temporaires qu'a
la fin du bloc main. Possible que ce soit parce que tu declares f et g
comme des references const dont la semantique a persiste au dela du
constructeur (post-inlining: le bloc du constructeur a ete etendu au
bloc du main). Mais c'est de la cuisine interne au compilateur.


oui

Normalement les temporaires references sans const sont detruits a la fin
de l'expression. Avec le const, la duree de vie est etendue jusqu'a la
fin du bloc.


ok

#include <algorithm>
#include <cassert>

struct Fonction {
virtual double operator()(double x) const = 0;
virtual ~Fonction(){};
};

class Affine : public Fonction {
double a;
public:
Affine(double a):a(a){};
double operator()(double x) const {
return a*x;
}
};

class Min : public Fonction {
const Fonction& f;
const Fonction& g;


Probleme ici. Il ne faut pas utiliser des references mais des copies.


Avec des copies
const Fonction f;
je perds le polymorphisme (et sur ce coup, il se plaint que Fonction
a une méthode virtuelle pure).

Alors je pourrais sortir l'artillerie des pointeurs, ou de methode
.clone(), mais j'ai la flemme. C'est du code jetable.

public:
Min(const Fonction& f, const Fonction& g):
f(f), g(g){};
double operator()(double x) const {
return std::min( f(x), g(x) );
}
};

int main(){
Min m( Affine(2), Affine(3) );
assert( m(1)== 2 );
Min m2( Min(Affine(2), Affine(3)), Affine(4) );
assert( m(1)== 2 );
}


En revanche, je ne vois pas le rapport avec 'pure virtual call' dans cet
exemple.


Le problème n'apparaît en effet pas sur cet exemple.
Le vrai problème y est-il lié ? Je ne sais pas. Parfois, il est plus
facile de corriger une erreur que de trouver sa cause exacte. Là,
j'ai remplacé tous mes


Min m( Affine(2), Affine(3) );
par des
Min m( *(new Affine(2)), *(new Affine(3)));
et ça marche.

Si le véritable exemple t'intéresse, je peux te le poster.
Ca n'est pas très long (7 classes, 400 lignes, 10ko de code).

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)


Avatar
Jean-Marc Bourguet
Marc Boyer writes:

Le 07-03-2007, Laurent Deniau a écrit :
Probablement parce que ton compilateur ne detruit les temporaires qu'a
la fin du bloc main. Possible que ce soit parce que tu declares f et g
comme des references const dont la semantique a persiste au dela du
constructeur (post-inlining: le bloc du constructeur a ete etendu au
bloc du main). Mais c'est de la cuisine interne au compilateur.


oui

Normalement les temporaires references sans const sont detruits a la fin
de l'expression. Avec le const, la duree de vie est etendue jusqu'a la
fin du bloc.


ok

#include <algorithm>
#include <cassert>

struct Fonction {
virtual double operator()(double x) const = 0;
virtual ~Fonction(){};
};

class Affine : public Fonction {
double a;
public:
Affine(double a):a(a){};
double operator()(double x) const {
return a*x;
}
};

class Min : public Fonction {
const Fonction& f;
const Fonction& g;


Probleme ici. Il ne faut pas utiliser des references mais des copies.


Avec des copies
const Fonction f;
je perds le polymorphisme (et sur ce coup, il se plaint que Fonction
a une méthode virtuelle pure).

Alors je pourrais sortir l'artillerie des pointeurs, ou de methode
.clone(), mais j'ai la flemme. C'est du code jetable.

public:
Min(const Fonction& f, const Fonction& g):
f(f), g(g){};
double operator()(double x) const {
return std::min( f(x), g(x) );
}
};

int main(){
Min m( Affine(2), Affine(3) );
assert( m(1)== 2 );
Min m2( Min(Affine(2), Affine(3)), Affine(4) );
assert( m(1)== 2 );
}


En revanche, je ne vois pas le rapport avec 'pure virtual call' dans cet
exemple.


Le problème n'apparaît en effet pas sur cet exemple.
Le vrai problème y est-il lié ? Je ne sais pas. Parfois, il est plus
facile de corriger une erreur que de trouver sa cause exacte. Là,
j'ai remplacé tous mes


Min m( Affine(2), Affine(3) );
par des
Min m( *(new Affine(2)), *(new Affine(3)));
et ça marche.

Si le véritable exemple t'intéresse, je peux te le poster.
Ca n'est pas très long (7 classes, 400 lignes, 10ko de code).


Tu peux le faire ou me l'envoyer en perso. Je ne garanti pas de reponse
rapide.

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



Avatar
Laurent Deniau
Marc Boyer wrote:
Probablement parce que ton compilateur ne detruit les temporaires qu'a
la fin du bloc main. Possible que ce soit parce que tu declares f et g
comme des references const dont la semantique a persiste au dela du
constructeur (post-inlining: le bloc du constructeur a ete etendu au
bloc du main). Mais c'est de la cuisine interne au compilateur.


oui

Normalement les temporaires references sans const sont detruits a la fin
de l'expression. Avec le const, la duree de vie est etendue jusqu'a la
fin du bloc.


ok

#include <algorithm>
#include <cassert>

struct Fonction {
virtual double operator()(double x) const = 0;
virtual ~Fonction(){};
};

class Affine : public Fonction {
double a;
public:
Affine(double a):a(a){};
double operator()(double x) const {
return a*x;
}
};

class Min : public Fonction {
const Fonction& f;
const Fonction& g;
Probleme ici. Il ne faut pas utiliser des references mais des copies.



Avec des copies
const Fonction f;
je perds le polymorphisme (et sur ce coup, il se plaint que Fonction
a une méthode virtuelle pure).


ok, j'avais rate ce details.

Alors je pourrais sortir l'artillerie des pointeurs, ou de methode
.clone(), mais j'ai la flemme. C'est du code jetable.

public:
Min(const Fonction& f, const Fonction& g):
f(f), g(g){};
double operator()(double x) const {
return std::min( f(x), g(x) );
}
};

int main(){
Min m( Affine(2), Affine(3) );
assert( m(1)== 2 );
Min m2( Min(Affine(2), Affine(3)), Affine(4) );
assert( m(1)== 2 );
}
En revanche, je ne vois pas le rapport avec 'pure virtual call' dans cet

exemple.


Le problème n'apparaît en effet pas sur cet exemple.
Le vrai problème y est-il lié ? Je ne sais pas. Parfois, il est plus
facile de corriger une erreur que de trouver sa cause exacte. Là,
j'ai remplacé tous mes


Min m( Affine(2), Affine(3) );
par des
Min m( *(new Affine(2)), *(new Affine(3)));
et ça marche.

Si le véritable exemple t'intéresse, je peux te le poster.
Ca n'est pas très long (7 classes, 400 lignes, 10ko de code).


Non, pas vraiment. Je sais comment produire facilement des 'pure virtual
call' tout seul comme un grand ;-) Et en plus je suis a la bourre pour
mon papier OOPSLA (date de cloture le 19 mars, sic!) donc d'ici la...

a+, ld.



Avatar
Marc Boyer
Le 07-03-2007, Laurent Deniau a écrit :
Marc Boyer wrote:
class Min : public Fonction {
const Fonction& f;
const Fonction& g;
Probleme ici. Il ne faut pas utiliser des references mais des copies.



Avec des copies
const Fonction f;
je perds le polymorphisme (et sur ce coup, il se plaint que Fonction
a une méthode virtuelle pure).


ok, j'avais rate ce details.


Oui. En je pensais que tu me ferais un couplet sur le choix
inadéquat de C++ de privilégier la sémantique de valeur.
Je réalise que quand on veut le polymorphisme, on est
obligé de ce taper pleins de pointeurs, et qu'en fait,
Java est cohérent sur ce point.

Non, pas vraiment. Je sais comment produire facilement des 'pure virtual
call' tout seul comme un grand ;-) Et en plus je suis a la bourre pour
mon papier OOPSLA (date de cloture le 19 mars, sic!) donc d'ici la...


Il te reste 12 jours. Ca dépend s'il faut encore obtenir les
résultats ou juste mettre en forme le papier...

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)




Avatar
Marc Boyer
Le 07-03-2007, Jean-Marc Bourguet a écrit :
Marc Boyer writes:

Si le véritable exemple t'intéresse, je peux te le poster.
Ca n'est pas très long (7 classes, 400 lignes, 10ko de code).


Tu peux le faire ou me l'envoyer en perso. Je ne garanti pas de reponse
rapide.


Levons une ambiguïté: en ce qui me concerne, l'explication sur
la gestion des temporaires me convient, en j'ai une solution à
base de *(new X(1)), qui crée des objets persistants.
Après, Laurent était étonné par la nature du message d'erreur et
je lui proposais le code s'il le voulait. Cela vaut aussi pour
toi.
Mais ne fait pas ça pour moi, mais uniquement pour satisfaire ta
curiosité.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)


Avatar
Sylvain
Marc Boyer wrote on 07/03/2007 13:42:

class Min : public Fonction {
const Fonction& f;
const Fonction& g;
Probleme ici. Il ne faut pas utiliser des references mais des copies.




des copies locales ou des références à des non-temporaires.

Avec des copies
const Fonction f;
je perds le polymorphisme (et sur ce coup, il se plaint que Fonction
a une méthode virtuelle pure).


le polymorphisme existe toujours mais en effet 'Fonction' est virtuel pure.

Alors je pourrais sortir l'artillerie des pointeurs, ou de methode
..clone(), mais j'ai la flemme. C'est du code jetable.


des données membre pointeurs sont pourtant la bonne solution, leur
initialisation, utilisation peut selon le nombre d'occurrence être
doublonné avec des refs pour s'épargner qlq déréférencements.

int main(){
Min m( Affine(2), Affine(3) );
assert( m(1)== 2 );
Min m2( Min(Affine(2), Affine(3)), Affine(4) );
assert( m(1)== 2 );
}
En revanche, je ne vois pas le rapport avec 'pure virtual call' dans cet

exemple.



à la sortie du constructeur de 'm' les tempos Affine(2) et (3) sont
détruits et le destructeur peut (doit?) réduire sa table de résolution
virtuelle au type de base, m.f et m.g existent toujours et contiennent
les ghosts de ces classes lors de l'appel à m(1), la résolution
virtuelle de () appliquée à f et g tombe alors sur la définition de
Fonction () qui est bien virtuelle pure.

Le problème n'apparaît en effet pas sur cet exemple.
Le vrai problème y est-il lié ? Je ne sais pas. Parfois, il est plus
facile de corriger une erreur que de trouver sa cause exacte. Là,
j'ai remplacé tous mes

Min m( Affine(2), Affine(3) );
par des
Min m( *(new Affine(2)), *(new Affine(3)));
et ça marche.


normal car les 2 Affine allouées ne sont jamais détruits, les refs (au
sens pointeur commode) f et g de m pointeront toujours sur ces valeurs.

"toujours" est peut être à nuancer, ce point a été discuté dans un fil
pas si vieux sur les références (décrits comme des faux-pointeurs par
"Dieu" et moi-même), ici f et g ont bien un rôle de pointeurs à des
références externes, elles doivent donc ne pas être temporaires.

Sylvain.



Avatar
Marc Boyer
Le 07-03-2007, Sylvain a écrit :
Avec des copies
const Fonction f;
je perds le polymorphisme (et sur ce coup, il se plaint que Fonction
a une méthode virtuelle pure).


le polymorphisme existe toujours mais en effet 'Fonction' est virtuel pure.


Le polymorphisme existe toujours ? Tu peux développer ?

Alors je pourrais sortir l'artillerie des pointeurs, ou de methode
..clone(), mais j'ai la flemme. C'est du code jetable.


des données membre pointeurs sont pourtant la bonne solution, leur
initialisation, utilisation peut selon le nombre d'occurrence être
doublonné avec des refs pour s'épargner qlq déréférencements.


Oui, si ce code avait vocation à durer, je ferais ça.

int main(){
Min m( Affine(2), Affine(3) );
assert( m(1)== 2 );
Min m2( Min(Affine(2), Affine(3)), Affine(4) );
assert( m(1)== 2 );
}
En revanche, je ne vois pas le rapport avec 'pure virtual call' dans cet

exemple.



à la sortie du constructeur de 'm' les tempos Affine(2) et (3) sont
détruits et le destructeur peut (doit?) réduire sa table de résolution
virtuelle au type de base, m.f et m.g existent toujours et contiennent
les ghosts de ces classes lors de l'appel à m(1), la résolution
virtuelle de () appliquée à f et g tombe alors sur la définition de
Fonction () qui est bien virtuelle pure.


D'ou le plantage.

Min m( Affine(2), Affine(3) );
par des
Min m( *(new Affine(2)), *(new Affine(3)));
et ça marche.


normal car les 2 Affine allouées ne sont jamais détruits, les refs (au
sens pointeur commode) f et g de m pointeront toujours sur ces valeurs.


Nous sommes d'accord.

"toujours" est peut être à nuancer, ce point a été discuté dans un fil
pas si vieux sur les références (décrits comme des faux-pointeurs par
"Dieu" et moi-même), ici f et g ont bien un rôle de pointeurs à des
références externes, elles doivent donc ne pas être temporaires.


Je viens de parcourir la discussion, et j'ai pas tout saisi.
Faudrai que je relise un jour où j'aurais plus dormis la nuit
précédente.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)




1 2 3 4