Je souhaiterais que la valeur retour d'une fonction soit une paire comme cecimais ça ne compile pas.Code:std::pair<T &,bool>
A l'origine la fonction renvoit une reference seule, mais j'aimerais y ajouter un bool. Comment faire ?
Merci.
Version imprimable
Je souhaiterais que la valeur retour d'une fonction soit une paire comme cecimais ça ne compile pas.Code:std::pair<T &,bool>
A l'origine la fonction renvoit une reference seule, mais j'aimerais y ajouter un bool. Comment faire ?
Merci.
Tu peut passer par un paramettre donnée-résultat. Du genre :
Code:T& fonction(int i, bool& b)
Bonjour,
le plus simple étant bien souvent le moins compliqué, et un typedef ne pouvant pas être template, je serais toi, je passerais par une petite structure. Un truc dans le style:
Code:
1
2
3
4
5
6
7
8
9
10 template <typename T> struct boolean_pair { T & Ref; bool Flag; }; // et du coup la fonction aura cette tête: template <typename T> boolean_pair<T> ma_fonction( /* parametres */ );
Salut,
Effectivement, la norme indique bien comme constructeur pair(const T1& x, const T2& y), ce qui de facto t'empêche d'avoir T1 ou T2 comme T&...
Sinon, la solution Rod est la plus simple. Mais généralement, ce genre de cas veut dire : si bool est vrai, alors T est valide et si bool est faux alors T n'est pas valide. Boost offre Boost.Optional pour résoudre ce genre de chose :
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 template<class T> boost::optional<T&> ma_fonction(T&p_) { //... if(ok) { return boost::optional<T&>(p_); } return boost::optional<T&)(); } // utilisation : boost::optional<T&> ret = ma_fonction(param); if(ret){// ok! T&resultat = *ret; } else{//ko }
Dans mon cas bool==true veut dire "T & est nouveau", bool==false veut dire "T & existe déjà" (un peu comme map::insert()).
Sinon, ce petit débat pour tester la valeur retour d'une fonction est intéressant. J'ai lu il n'y a pas très longtemps "les solutions les plus simples sont les moins compliquées" (:lol:;)) et le plus simple est de renvoyer un... pointeur. S'il est nul la valeur est invalide.
Il m'est d'ailleurs déjà arrivé de faire la même chose avec des références pour lesquelles il suffit de tester leur adresse:
T & ref=foo();
if (&ref) valide; else invalide;
mais ce n'est pas très sécurisant je le reconnais.
Il semble que boost::optional conceptualize cet aspect des choses. Mais s'il ne fait "que" ça, c'est un peu lourd quand même...
Je sais que beaucoup ici n'aime pas les pointeurs et cherche à tout prix à les cacher, mais le pointeur reste une base incontournable du C++ (abondamment utilisé il me semble dans la STL, boost, etc).
Pour en revenir à mon petit soucis de conception, je pense que la solution de r0d semble la plus élégante.
Concrêtement je suis en train d'écrire un cache template basique. Lorsque l'on requiert une entrée du cache via une clé on a besoin de savoir si l'entrée est nouvelle ou si elle existe déjà. Il me reste un autre petit soucis, comment faire savoir à l'appelant qu'une entrée du cache va être supprimée (je pense à un callback, un peu comme un prédicat).
Ca veut dire quoi lourd ? Le bout de code que j'ai mis en exemple me semble pourtant 'simple', non?
Et une base incontournable de la plus part des bugs ;)
Sans boost, c'est surtout la plus simple.
Bizarre. J'aurais tendance à penser que le cache est celui qui sert l'objet, donc il sait s'il peut le fournir tout de suite ou s'il doit aller le chercher. Peux-tu préciser ta conception ?
[EDIT]: ou au - celui qui sert l'objet a le cache et l'interroge pour savoir s'il contient l'objet, sinon, il va le lire et le rajoute au cache.
La méthode fetch() renvoit l'entrée dans le cache correspondant à une clé, en plus d'un booléen pour savoir si l'entrée est créée ou si elle existait déjà. Reste que si l'entrée lru a été rétirée, on ne le sait pas (encore).Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 template <typename K, typename T> class cache { ... public: struct cell_value { T & s_value; bool s_created; cell_value(T & value,bool created):s_value(value),s_created(created) {} cell_value(cell_value const & r):s_value(r.s_value),s_created(r.s_created) {} cell_value & operator=(cell_value const & r) { s_value=r.s_value;s_created=r.s_created;return *this; } }; cache(unsigned maxCell); ~cache() { clear(); } void clear(); void reset(unsigned maxCell); cell_value fetch(K key); };
L'entrée LRU (la plus vieille quoi).
Le cache est une collection de cellules en nombre limité. Ces cellules contiennent une clé K et une tampon T.
L'utilisateur demande au cache de lui fournir un tampon T pour une clé K donnée. Le cache ne refuse jamais, il renvoit pour cela une référence sur ce tampon T correspondant à la clé K, et un booléen pour informer si ce tampon vient d'être créé ou s'il existait.
Comme le cache possède un nombre limité de cellules et qu'il ne refuse jamais une nouvelle cellule, il se peut qu'il doive en supprimer une, à priori la plus ancienne (il peut y avoir d'autres stratégies). C'est cette cellule éventuellement supprimée qui doit pouvoir être notifiée à l'utilisateur via un "callback" juste avant sa suppression effective du cache (et donc avant le retour de la fonction fetch).
Il suffirait de rajouter une méthode qui teste une clé et qui renvoit une référence sur le tampon qui serait supprimé le cas échéant. Mais je préfère une solution avec "callback" (un peu à la manière des prédicats de la STL). Elle permet d'éviter de rechercher deux fois la clé, une fois lors du test, une deuxième fois lors du fetch().
En bref, le rôle de l'utilisateur consiste simplement à appeler la méthode fetch() pour récupérer le tampon dans le cache correspondant à sa clé: il initialise ce tampon s'il vient d'être créé, il l'utilise sinon. Rien de plus, l'info se trouvant dans la valeur retour de fetch() (<T&,bool>).
Mais parfois (et pas toujours), l'utilisateur a besoin de savoir si le cache a dû faire de la place.
Ok, compris.
Je recopie un extrait de mon codeIl y a quelque chose qui ne va pas, l'operator= d'assignation est incorrect.Code:
1
2
3
4
5
6
7
8
9
10 struct cell_value { T & s_value; bool s_created; cell_value(T & value,bool created):s_value(value),s_created(created) {} cell_value(cell_value const & r):s_value(r.s_value),s_created(r.s_created) {} cell_value & operator=(cell_value const & r) { s_value=r.s_value;s_created=r.s_created;return *this; } };
En fait il est impossible de changer une référence.Du coup la solution proposée par r0d ne convient pas ?Code:
1
2
3
4
5 int i=3; int & rint=i; int j=2; rint=j;//rint ne prend pas une référence sur j, // mais modifie i avec la valeur de j !!!
Tu vas devoir passer par boost::optional :mouarf:...
Bah, non ;)
Je supprime l'assignation pour forcer la création d'une variable locale, comme pour toute référence. Et ce n'est pas plus mal finalement.
Je regarde ta classe et je ne comprend pas pourquoi tu redéfinis le constructeur par copie et l'opérateur d'assignation. La version générée par le compilateur par défaut devrait te suffire non?
Oui, j'essaie ça.
Mais j'ai encore quelques soucis à propos des "big fours", je ne sais jamais trop quel est le comportement par défaut...
Faudra que j'ouvre une discussion à ce propos un de ces quatre :oops:
Je dirais que tant que ça consiste à faire une bête recopie de tes attributs, ben le fait pas. Comme le destructeur :si vide et pas d'héritage, ben pas de destructeur.
Les destructeurs, memes vides, c'est pratique pour mettre des points d'arret.
Tu ne peux pas mettre de référence dans un tuple ou dans un container.
Il faut donc utiliser boost::reference_wrapper à la place, ou accessoirement un pointeur.