Le code C ++ suivant ne se compile pas car il transmet un pointeur non-const à une fonction find ()
qui attend un pointeur const.
#include <map> std::map<int*, double> mymap; double myfind(const int * mykey) { return mymap.find(mykey)->second; }
Y a-t-il un moyen de faire fonctionner la recherche sans changer le type de la carte ou rendre la variable mykey
non-const?
Après tout, la fonction find ()
ne modifie pas l'objet pointé, elle compare simplement les pointeurs.
4 Réponses :
Essayez const_cast
qui vous permet de changer la constance (ou la volatilité) de la variable.
#include <map> std::map<int*, double> mymap; double myfind(const int * mykey) { return mymap.find(const_cast<int*>(mykey))->second; }
Oui mais const_cast
est généralement le signe d'une mauvaise interface
Je ne recommande aucun casting, mais c'est une possibilité;)
Une clé dans une carte est sémantiquement immuable, toutes les opérations de carte qui permettent un accès direct aux clés le font en En cas de type de clé const
-qualifiant le type de clé (par exemple value_type
est défini comme paire
), ce qui n'est pas très sympa (ça marche toujours, puisque seul le pointeur value est utilisé comme clé, mais la sémantique de l'immuabilité se dilue, ce qui peut conduire à des bogues). int *
, vous obtiendrez un pointeur const
vers non-const int
( int * const
Au lieu de rejeter constness, changez simplement la map
en map
.
Ensuite, cela fonctionnera pour les clés const int *
ainsi que int *
.
#include <map> std::map<const int*, double> mymap; double myfind(const int * mykey) { return mymap.find(mykey)->second; // just works } double myfind(int * mykey) { return mymap.find(mykey)->second; // also works }
La valeur sémantique de la clé ne peut pas être modifiée de manière arbitraire dans un conteneur associatif car le conteneur utilise la comparaison (resp. Le hachage) pour ordonner les objets (resp. Les stocker dans un tableau). Mais la valeur sémantique de la clé ici est la valeur du pointeur. Vous pouvez changer le int
sans casser le conteneur.
Vous pouvez avoir un problème de const-correctness. const int *
n'est peut-être pas ce que vous pensez . Il s'agit d'un pointeur vers un entier constant . Ce n'est pas le même que le type de clé de votre carte, qui est un pointeur vers un entier (non constant) . Et ni sont identiques à int * const
qui est un pointeur constant vers un entier (non constant) . Le problème n'est pas de savoir si la valeur de la clé elle-même est mutable ou immuable, c'est si les éléments sur lesquels vous stockez les pointeurs sont mutables ou immuables.
Par exemple, ceci compile:
std::map<const int *, double> mymap; double myfind(const int *mykey) { return mymap.find(mykey)->second; } double myfind2(const int * const mykey) { return mymap.find(mykey)->second; }
Je comprends la différence entre const int *
et int * const
. Mais vous avez fait un bon point dans le dernier paragraphe. Nous savons que std :: map :: find ()
ne modifiera pas l'entier pointé, mais le compilateur ne le sait pas. Et dans cette question, j'ai demandé comment le faire savoir au compilateur.
@bedrorom et la réponse à cela se trouve également dans le dernier paragraphe: const_cast<>
Oui, const_cast<>
est une possibilité multiple. Mais rejeter const
est n'est pas une bonne pratique . Un comparateur personnalisé (si disponible) peut faire la même chose sans enfreindre les recommandations.
Tout à fait d'accord, ce n'est pas une bonne pratique, mais je pense qu'à moins que vous ne changiez le type de clé de la map
, c'est finalement ce que vous faites, quels que soient les obstacles que vous franchissez pour y arriver. Le cœur de ma réponse, qui je pense est importante et valable pour les lecteurs, est que const int *
est une chose sémantiquement différente de int *
. A cela je n'ai vraiment rien à ajouter ou à apporter; Je regrette que cela n'ait pas plu à l'électeur défavorable.
Je pense avoir trouvé une solution, mais elle nécessite des comparateurs transparents C ++ 14.
#include <map> #include <iostream> struct CompareIntPtrs { using is_transparent = void; // enabling C++14 transparent comparators bool operator()(const int * l, const int * r) const { return l < r; } }; std::map<int*, double, CompareIntPtrs> mymap; double myfind(const int * key) { return mymap.find(key)->second; } int main() { int x {6}; mymap[&x] = 66; // inserting to the map const int * px = &x; // creating a "const int *" variable std::cout << myfind(px) << std::endl; // using "const int *" for finding in map with "int*" keys std::cout << mymap.find(px)->second << std::endl; // we could even skip using myfind() }
Un excellent article sur les comparateurs transparents C ++ 14 peut être trouvé ici .
Pour être tout à fait honnête, en ajoutant le comparateur, le type de mymap
a légèrement changé ce que je ne voulais pas à l'origine, mais c'est la meilleure solution que j'ai pu trouver.
Si C ++ 14 n'est pas disponible, il y a au moins deux maux parmi lesquels nous pouvons choisir.
Le premier est de copier mymap
dans un nouveau std :: map
dans myfind
, ce qui est horriblement inefficace.
Le second est de rejeter la constness en utilisant un const_cast
qui devrait être évité si possible.
Utilisez
const_cast
@ t.niese Pourquoi changeriez-vous cela? Comment résoudrait-il le problème sous-jacent?
Après de nombreuses années, les défauts des interfaces STL sont toujours là; esp. le typage des conteneurs associatifs, le manque de recherche binaire ...
Pourquoi ne pas changer le type de clé à la place?
@curiousguy pas avec les comparateurs transparents de C ++ 14. C'est la différence entre
std :: less
etstd :: less
@Caleth Vous m'avez orienté dans la bonne direction en mentionnant des comparateurs transparents.