surcharges const, non const, et optims du compilo

surcharges const, non const, et optims du compilo - C++ - Programmation

Marsh Posté le 12-12-2007 à 04:27:01    

Bonjour à tous :)
 
Il y a quelques temps je codais un conteneur à taille variable.
 
La fonction:

Code :
  1. size_t length() const;


retourne la taille du conteneur.
 
La fonction:

Code :
  1. Resizer length();


retourne un objet qui contient un pointeur vers le conteneur, et pour lequel l'opérateur = (size_t) modifie la taille, et l'opérateur size_t () const retourne la taille.
 
Ainsi on peut écrire:

Code :
  1. Container c;
  2. c.length()=10;
  3. for(size_t i=0;i<c.length();i++)
  4. { c[ i ]=rand(); }


 
Seulement, le compilateur, dans ce cas particulier, choisit à la ligne 3 l'operateur size_t () const du Resizer, sans jamais utiliser Container::length() const, puisque Container n'est pas déclaré const.
 
La solution de contourner le problème en utilisant des fonctions setLength() et getLength() const ne me plaît pas vraiment, puisqu'elle n'est pas applicable par exemple si au lieu d'une fonction length() j'aurais eu besoin d'un opérateur.
 
La solution de caster le Container en (const Container& ) annule le bénéfice de la syntaxe allégée lors de l'utilisation de length() const.
 
La solution de supprimer length() const et ne garder que Resizer:: operator size_t () const ne me plaît pas vraiment non plus, puisque la simple lecture de la taille est déclarée comme modifiant le conteneur (et pourrait par ailleurs réellement le modifier en faisant un pré-traitement sur le conteneur).
 
La solution de déclarer une classe Resizer et une autre ConstResizer pose le même problème.
 
Les solutions de tout inliner, ou d'utiliser une variable supplémentaire pour stocker une copie de la taille (10), sont plutôt limites aussi.
 
:pt1cable: Bref.. le code étant petit et le conteneur ne nécessitant pas de pré-traitement, j'ai tout inliné finalement.
Mais je me pose toujours la question: Comment demander au compilo d'utiliser length() const plutot que length()?
 
Merci pour vos éclaircissements. :jap:


Message édité par nargy le 12-12-2007 à 04:28:20
Reply

Marsh Posté le 12-12-2007 à 04:27:01   

Reply

Marsh Posté le 12-12-2007 à 10:25:49    

Il n'y a pas de problème, c'est tout a fait normal comme comportement et c'est défini.
 
Si tu veux changer le comportement, change ta méthode de nom ou fait un const_cast ignoble.

Reply

Marsh Posté le 12-12-2007 à 11:39:57    

ha ouais.. je comprends bien le comportement du compilo, ça me paraît tout à fait normal. J'ai dû trop faire de C#, et j'essaye de reproduire ses getters et setters...
 
Le cast est ignoble, oui.
 
J'en conclu: pas la peine de cherche midi à 14H, la seule méthode potable finalement c'est setLength() et getLength(). Exit donc les opérateurs, notamment operator [] qui ne peut pas servir de getter et setter dans une table de hachage ou autre tableau dynamique.
 
Merci Taz

Reply

Marsh Posté le 12-12-2007 à 11:43:34    

- Fais un proxy.
- fait length() const et length(size_t len);

Reply

Marsh Posté le 12-12-2007 à 11:51:16    

nargy a écrit :


Exit donc les opérateurs, notamment operator [] qui ne peut pas servir de getter et setter dans une table de hachage ou autre tableau dynamique.


 
et à quoi il sert dans std::map ??? ou alors j'ai aps compris la question [:dawa]

Reply

Marsh Posté le 12-12-2007 à 13:30:31    

C'est ce qu'est la classe "Resizer" dans mon exemple: un proxy.
 
Ça fonctionne, avec std:map aussi, ce qui me gène c'est que le proxy n'est jamais const, ou alors il faut le préciser avec un cast (ça se fait d'ailleurs plutôt avec une classe ConstProxy il me semble).
 
Exemple: les données dans le conteneur sont en réalité accesible par connexion réseau. Lors de la création du proxy, on peut être (presque) sûr que la connexion réseau devra être ouverte pour connaître ou modifier la taille du conteneur. Par contre, pour modifier la taille il faut passer la connexion réseau en mode sécurisée deux fois plus lente. La connexion persistante est une propriété de la classe Container.
 
Mais lorsque l'on écrit:

Code :
  1. void auto_agrandir_conteneur(Container& c, size_t n)
  2. { if(n>c.length()) c.length()=n; }


une connexion sécurisée est toujours ouverte plutot que: une connexion normale est ouverte, puis passage en sécurisé seulement si la taille est plus petite que 'n'.
 
Celà n'empêche pas std::map de fonctionner de manière optimale, ni l'exemple que je donne au début, puisque le compilo va de toutes façon aller regarder dans le code inliné, et faire les optims necessaires parceque tout se passe en mémoire et dans les registres. Mais dans des cas plus complexes... le compilo n'a pas accès à la connexion réseau par exemple pour déterminer qu'il n'y a en fait pas besoin d'ouvrir une connexion sécurisée.
 
ou alors, il faut préciser:

Code :
  1. void auto_agrandir_conteneur(Container& c, size_t n)
  2. { if(n>(ConstResizer)c.length()) c.length()=n; }


et c'est là que je trouve ça un poil surchargé et buggogène (la fonction length() semble s'occuper de tout d'aspect extérieur). Du coup la solution de donner des noms distincts aux deux fonctions length() est plus pratique. Idem, length(int), même si plus court syntaxiquement, est contre-intuitif (pourquoi deux fonctions length()??), alors que l'opérateur = ou la fonction setLength(int) sont absolument explicites.
 
Edit: ça y est, je comprends mon erreure.. le proxy dont tu parles c'est sur le conteneur qu'il faut le placer, pas sur sa taille.


Message édité par nargy le 13-12-2007 à 09:29:05
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed