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

Mutable ou non...

10 réponses
Avatar
Michel Michaud
Bonjour les amis :-)

(sachez que même si je n'écris pas souvent, je lis tout ce qui se
passe ici, mais je me trouve un peu en retard pour répondre, j'en
suis aujourd'hui à lire les messages de la fin janvier. Ceci dit,
je vais lire rapidement les réponses à ce message !)

Même si je comprends très bien les raisons de l'existence de
mutable, son emploi, sa sémantique, etc., je n'ai jamais vraiment
eu de cas réels où il m'aurait été utile. Par conséquent, le cas
qui suit, très simple, me laisse perplexe et je me demande s'il y
a un autre moyen...

Pour simplifier, disons que j'ai une classe qui contient et gère
des données récupérées d'un fichier. Une fonction permet
d'enregistrer les modifications et elle est appelée automatiquement
par le destructeur :

class Données
{
public :
Données(); // Récupère les données d'un fichier

void ModifierLesDonnées(); // Et autres fonctions de modif.
void Afficher() const; // Et autres de consultation

void Enregistrer() const; // Enregistre dans le fichier

~Données()
{
Enregistrer();
// Et d'autres opérations probablement
}

// ...
};

Les clients peuvent appeler Enregistrer() s'ils ne veulent pas
attendre l'enregistrement fait par la destruction.

Maintenant pour optimiser, on décide d'ajouter un bool, genre
« estModifié » qui sera mis à false au départ, mais mis à true
par les fonctions de modification. On voudra alors faire :

void Données::Enregistrer() const
{
if ( ! estModifié)
return;

// ... Le code pour l'enregistrement

estModifié= false;
}

Pour moi, clairement, la fonction est conceptuellement const et
il faut donc que estModifié soit mutable. Ceci dit, ça me paraît
difficile de croire que ce cas n'arrivait à personne avant que
l'on invente mutable, alors je me demande s'il y a une autre
solution, moins « C++ très moderne » et sans abus du langage.
Peut-être que quelque chose m'échappe. Personnellement, je n'avais
pas tendance à enseigner mutable aux débutants (de peur qu'ils en
abusent), mais là, je me pose la question... Dois-je attendre au
chapitre 17 de mon livre (le dernier) avant de parler de mutable,
si mes exemples tout simples semblent parfois l'exiger ?

--
Michel Michaud mm@gdzid.com
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

10 réponses

Avatar
Sylvain
Michel Michaud wrote on 25/04/2006 05:18:

Pour moi, la fonction est conceptuellement const et
il faut donc que estModifié soit mutable.


pour moi elle est conceptuellement non const puisque qu'elle change
l'état de l'instance (ne serait-ce que d'un 'dirty' flag), dans la
réalité une fonction de sérialisation pourra désallouer des temporaires,
purger un buffer qui aurait grossi au dela d'une valeur d'usage, effacer
et déréférencer un fichier temporaire, incrémenter un numéro de version
de l'instance, etc, etc, non, pas const tout ça. (on trouve parfois ce
mauvais exemple du carnet d'adresses où le load serait non const et le
save const, c'est un très mauvais exemple).

une fonction estEnregistrable serait surement const car elle dépendrait
de l'instance mais ne serait pas censé la modifier et de son
environnement (le disque target par exemple).

difficile de croire que ce cas n'arrivait à personne avant que
l'on invente mutable, alors je me demande s'il y a une autre
solution, moins « C++ très moderne » et sans abus du langage.


ils conceptuallisaient peut être pas de la même façon.

Peut-être que quelque chose m'échappe. Personnellement, je n'avais
pas tendance à enseigner mutable aux débutants (de peur qu'ils en
abusent), mais là, je me pose la question... Dois-je attendre au
chapitre 17 de mon livre (le dernier) avant de parler de mutable,
si mes exemples tout simples semblent parfois l'exiger ?


l'attribut mutable me parait évident comme renfort _et_ contournement à
la règle: l'attribut const est évident de sens, cependant il est plus ou
moins un artifice d'écriture et on peux le 'violer', mutable offre un
passe-droit dans les règles (plus besoin de transcaster salement).

et les exemples peuvent êtres simples, si une instance veut mémoriser le
nombre de lecture (donc const) d'une de ses données, on écrira volontiers:

class X {
Z z;
mutable long c;
...
public:
Z getZ() const { ++c, return z; }
};

Sylvain.

Avatar
Loïc Joly


Maintenant pour optimiser, on décide d'ajouter un bool


C'est en général dans ce genre de situation que je me suis retrouvé avec
du mutable. Je ne vois qu'une alternative : Dire que rien n'est const...

--
Loïc

Avatar
kanze
Michel Michaud wrote:
Bonjour les amis :-)


Tiens, un revenant. Bienvenu de retour.

Même si je comprends très bien les raisons de l'existence de
mutable, son emploi, sa sémantique, etc., je n'ai jamais
vraiment eu de cas réels où il m'aurait été utile. Par
conséquent, le cas qui suit, très simple, me laisse perplexe
et je me demande s'il y a un autre moyen...

Pour simplifier, disons que j'ai une classe qui contient et
gère des données récupérées d'un fichier. Une fonction permet
d'enregistrer les modifications et elle est appelée
automatiquement par le destructeur :


Ce n'est pas bien, ça. Et qu'est-ce qui se passe en cas d'erreur
lors de l'enrégistrement ? Gérer des erreurs depuis un
destructeur, c'est assez délicat. (Mais bien, il y a des cas...
Si tu es sur un disque local, et que la taille du fichier
n'augmente pas, les cas d'erreur doivent être assez rare qu'on
puisse en faire abstraction. C'est juste pour te taquiner un
peu.)

class Données
{
public :
Données(); // Récupère les données d'un fichier

void ModifierLesDonnées(); // Et autres fonctions de modif.
void Afficher() const; // Et autres de consultation

void Enregistrer() const; // Enregistre dans le fichier

~Données()
{
Enregistrer();
// Et d'autres opérations probablement
}

// ...
};

Les clients peuvent appeler Enregistrer() s'ils ne veulent pas
attendre l'enregistrement fait par la destruction.

Maintenant pour optimiser, on décide d'ajouter un bool, genre
« estModifié » qui sera mis à false au départ, mais mis à true
par les fonctions de modification. On voudra alors faire :

void Données::Enregistrer() const
{
if ( ! estModifié)
return;

// ... Le code pour l'enregistrement

estModifié= false;
}

Pour moi, clairement, la fonction est conceptuellement const et
il faut donc que estModifié soit mutable.


Tout à fait.

Ceci dit, ça me paraît difficile de croire que ce cas
n'arrivait à personne avant que l'on invente mutable, alors je
me demande s'il y a une autre solution, moins « C++ très
moderne » et sans abus du langage.


Il y a eu des conversions explicites depuis le début du temps.
Quelque chose du genre :

((Données*)this)->estModifié = false ;

L'ARM en prevenait le cas, et permettait ce genre de manip, même
si l'objet même était const, à condition que le constructeur du
type était défini par l'utilisateur. (Et il m'en servait bien
une ou deux fois.)

Peut-être que quelque chose m'échappe. Personnellement, je
n'avais pas tendance à enseigner mutable aux débutants (de
peur qu'ils en abusent), mais là, je me pose la question...
Dois-je attendre au chapitre 17 de mon livre (le dernier)
avant de parler de mutable, si mes exemples tout simples
semblent parfois l'exiger ?


Si l'exemple l'exige, il vaut mieux le présenter. Mais je me
pose la question s'il ne vaut pas mieux changer l'exemple, pour
qu'il ne l'exige pas -- c'est en effet quelque chose de spécial,
qu'on n'a pas besoin de présenter aux débuttants.

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

Avatar
kanze
Loïc Joly wrote:

Maintenant pour optimiser, on décide d'ajouter un bool


C'est en général dans ce genre de situation que je me suis
retrouvé avec du mutable. Je ne vois qu'une alternative : Dire
que rien n'est const...


Même aujourd'hui, je me sers de const_cast dans certains 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


Avatar
Rémy
"Sylvain" a écrit dans le message de news:
444da558$0$6687$
Michel Michaud wrote on 25/04/2006 05:18:

Pour moi, la fonction est conceptuellement const et
il faut donc que estModifié soit mutable.


pour moi elle est conceptuellement non const puisque qu'elle change l'état
de l'instance (ne serait-ce que d'un 'dirty' flag),


Justement, toute la question est la définition de "l'état".

On peut considérer que l'état de l'objet n'a pas changé si d'un point de vue
externe (pour celui qui appelle des méthodes de l'objet), le comportement de
l'objet n'est pas modifié.

dans la réalité une fonction de sérialisation pourra désallouer des
temporaires, purger un buffer qui aurait grossi au dela d'une valeur
d'usage, effacer et déréférencer un fichier temporaire, incrémenter un
numéro de version de l'instance, etc, etc, non, pas const tout ça.


Et pourquoi pas const si, d'un point de vue externe, l'objet garantit que
son comportement n'est pas modifié ?

(on trouve parfois ce mauvais exemple du carnet d'adresses où le load
serait non const et le save const, c'est un très mauvais exemple).

une fonction estEnregistrable serait surement const car elle dépendrait de
l'instance mais ne serait pas censé la modifier et de son environnement
(le disque target par exemple).



C'est une définition possible de l'état de l'objet, mais je la trouve très
"informatique".

Dans le cas d'objets plus orientés "métier", on peut très bien considérer
que la modification de certains attributs n'est pas une modification de
"l'état" de l'objet.

difficile de croire que ce cas n'arrivait à personne avant que
l'on invente mutable, alors je me demande s'il y a une autre
solution, moins « C++ très moderne » et sans abus du langage.


ils conceptuallisaient peut être pas de la même façon.

Peut-être que quelque chose m'échappe. Personnellement, je n'avais
pas tendance à enseigner mutable aux débutants (de peur qu'ils en
abusent), mais là, je me pose la question... Dois-je attendre au
chapitre 17 de mon livre (le dernier) avant de parler de mutable,
si mes exemples tout simples semblent parfois l'exiger ?


l'attribut mutable me parait évident comme renfort _et_ contournement à la
règle: l'attribut const est évident de sens, cependant il est plus ou
moins un artifice d'écriture et on peux le 'violer', mutable offre un
passe-droit dans les règles (plus besoin de transcaster salement).



Je trouve plutôt que mutable permet d'affiner l'application de "const" au
cas par cas.

et les exemples peuvent êtres simples, si une instance veut mémoriser le
nombre de lecture (donc const) d'une de ses données, on écrira volontiers:

class X {
Z z;
mutable long c;
...
public:
Z getZ() const { ++c, return z; }
};



Très bon exemple.


Sylvain.




Avatar
Michel Michaud
Dans le message 444da558$0$6687$,
Michel Michaud wrote on 25/04/2006 05:18:

Pour moi, la fonction est conceptuellement const et
il faut donc que estModifié soit mutable.


pour moi elle est conceptuellement non const puisque qu'elle change
l'état de l'instance (ne serait-ce que d'un 'dirty' flag),


Mais avec une vision comme celle-là, mutable ne serait jamais utile.

[...]
l'attribut mutable me parait évident comme renfort _et_
contournement à la règle: l'attribut const est évident de sens,
cependant il est plus ou moins un artifice d'écriture et on peux le
'violer', mutable offre un passe-droit dans les règles (plus besoin
de transcaster salement).
et les exemples peuvent êtres simples, si une instance veut
mémoriser le nombre de lecture (donc const) d'une de ses données,
on écrira volontiers:
class X {
Z z;
mutable long c;
...
public:
Z getZ() const { ++c, return z; }


« pour moi elle est conceptuellement non const puisque qu'elle change
l'état de l'instance » :-)

Sérieusement la fonction devrait s'appeler getZandCountAccess et là,
elle n'a pas à être const.

Mais merci pour ton point de vue, qui permet de voir que ce n'est pas
tout le monde qui a le même.

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
Michel Michaud
Dans le message 444dc00d$0$164$,


Maintenant pour optimiser, on décide d'ajouter un bool


C'est en général dans ce genre de situation que je me suis retrouvé
avec du mutable. Je ne vois qu'une alternative : Dire que rien
n'est const...


Ce n'est pas une alternative, parce qu'il est impossible d'enlever
les const des paramètres des fonctions de bibliothèque par exemple.

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
adebaene



Même si je comprends très bien les raisons de l'existence de
mutable, son emploi, sa sémantique, etc., je n'ai jamais vraiment
eu de cas réels où il m'aurait été utile.


Un cas où mutable m'aide bien, c'est quand j'écris une classe
thread-safe qui a une donnée membre privée de type Lock (section
critique / mutex, etc...). Cet objet est généralement mutable, de
façon à pouvoir être acquis puis libéré dans des fonctions const
de la classe (le lock ne participe pas à l'état de l'objet, il est
juste là pour garntir la sérialisation des appels à l'objet).

Arnaud

Avatar
Michel Michaud
Dans le message ,
Michel Michaud wrote:
Bonjour les amis :-)


Tiens, un revenant. Bienvenu de retour.


Je suis toujours là, mais silencieux :-)

Pour simplifier, disons que j'ai une classe qui contient et
gère des données récupérées d'un fichier. Une fonction permet
d'enregistrer les modifications et elle est appelée
automatiquement par le destructeur :


Ce n'est pas bien, ça. Et qu'est-ce qui se passe en cas d'erreur
lors de l'enrégistrement ? Gérer des erreurs depuis un
destructeur, c'est assez délicat. (Mais bien, il y a des cas...
Si tu es sur un disque local, et que la taille du fichier
n'augmente pas, les cas d'erreur doivent être assez rare qu'on
puisse en faire abstraction. C'est juste pour te taquiner un
peu.)


Oui, mais j'ajouterai que la gestion des erreurs d'espace disque
(et autres du même genre) dépasse encore plus le niveau débutant
que mutable. En fait, j'ai l'impression que dans des programmes
où c'est très important, il est impossible d'utiliser <fstream>
ou les FILE*, car on voudra tout ce que le système d'exploitation
peut nous donner par l'utilisation des fichiers natifs.

[...]
Pour moi, clairement, la fonction est conceptuellement const et
il faut donc que estModifié soit mutable.


Tout à fait.


Venant de toi, ça me rassure :-)

[...]
Il y a eu des conversions explicites depuis le début du temps.
Quelque chose du genre :

((Données*)this)->estModifié = false ;

L'ARM en prevenait le cas, et permettait ce genre de manip, même


Donc ça se faisait, c'est ce que je voulais savoir. Merci !

[...]
Si l'exemple l'exige, il vaut mieux le présenter. Mais je me
pose la question s'il ne vaut pas mieux changer l'exemple, pour
qu'il ne l'exige pas -- c'est en effet quelque chose de spécial,
qu'on n'a pas besoin de présenter aux débuttants.


Oui, c'est un peu sur quoi je m'enligne probablement.

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
kanze
Michel Michaud wrote:
Dans le message ,
Michel Michaud wrote:



[...]
Pour simplifier, disons que j'ai une classe qui contient et
gère des données récupérées d'un fichier. Une fonction
permet d'enregistrer les modifications et elle est appelée
automatiquement par le destructeur :


Ce n'est pas bien, ça. Et qu'est-ce qui se passe en cas
d'erreur lors de l'enrégistrement ? Gérer des erreurs depuis
un destructeur, c'est assez délicat. (Mais bien, il y a des
cas... Si tu es sur un disque local, et que la taille du
fichier n'augmente pas, les cas d'erreur doivent être assez
rare qu'on puisse en faire abstraction. C'est juste pour te
taquiner un peu.)


Oui, mais j'ajouterai que la gestion des erreurs d'espace
disque (et autres du même genre) dépasse encore plus le niveau
débutant que mutable.


Gestion d'erreurs, ça peut être peu de choses. Un message
d'erreur, et sortir avec un état EXIT_FAILURE. Je trouve qu'il
serait bon, même, de le présenter aux débuttants -- deux lignes
à la fin de main :

std::cout.flush() ;
return std::cout ? EXIT_SUCCESS : EXIT_FAILURE ;

Mais je suis d'accord que d'aller beaucoup plus loin distrairait
souvent de la partie centrale de la leçon.

En fait, j'ai l'impression que dans des programmes où c'est
très important, il est impossible d'utiliser <fstream> ou les
FILE*, car on voudra tout ce que le système d'exploitation
peut nous donner par l'utilisation des fichiers natifs.


Tout dépend. En partie, tu as raison, au moins pour les choses
plus complex. Dans mes serveurs, j'ai des std::filebuf dans les
entrailles du log, mais sinon, c'est des ostringstream suivis
des write Unix (avec des options d'écriture qui ne sont pas
disponibles à l'interface filebuf, évidemment). De l'autre part,
pour de petits utilitaires, qui n'écrit que de façon
séquentielle, et où il n'y a pas de checkpoints, on peut s'en
tirer de façon adéquate.

[...]
Pour moi, clairement, la fonction est conceptuellement
const et il faut donc que estModifié soit mutable.


Tout à fait.


Venant de toi, ça me rassure :-)


Le débat entre const logique et const bit-à-bit est du passée,
je crois. Il me semble qu'il y a aujourd'hui un consensus pour
le const logique. Et je ne vois pas comment ta variable puisse
faire partie de l'état logique de l'objet -- c'est un détail de
l'implémentation, qui au fond n'intéresse personne (sauf
l'implémenteur, évidemment).

[...]
Il y a eu des conversions explicites depuis le début du temps.
Quelque chose du genre :

((Données*)this)->estModifié = false ;

L'ARM en prevenait le cas, et permettait ce genre de manip, même


Donc ça se faisait, c'est ce que je voulais savoir. Merci !


Je crois qu'il y en avait même un exemple dans les anciens
Stroustrup. Je l'ai bien appris de quelque part, après tout, et
je programmais en C++ depuis bien un an avant de trouver quelque
chose d'autre que le Stroustrup (première édition, évidemment)
sur le langage.

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