L'accès à la carte C ++ rejette les qualificatifs (Const)

113

Le code suivant indique que le fait de transmettre la carte comme constdans la operator[]méthode rejette les qualificatifs:

#include <iostream>
#include <map>
#include <string>

using namespace std;

class MapWrapper {
public:
    const int &get_value(const int &key) const {
        return _map[key];
    }

private:
    map<int, int> _map;
};

int main() {
    MapWrapper mw;
    cout << mw.get_value(42) << endl;
    return 0;
}

Est-ce à cause de l'attribution possible qui se produit sur l'accès à la carte? Aucune fonction avec accès à la carte ne peut être déclarée const?

MapWrapper.cpp:10: error: passing ‘const std::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >’ as ‘this’ argument of ‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = int, _Tp = int, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, int> >]’ discards qualifiers

cdleary
la source
Juste un pinaillage, mais mw peut être déclaré simplement comme MapWrapper mw;
lu
Bon point - j'écris dans quelques langues, j'ai donc tendance à normaliser la syntaxe entre elles pour qu'elles tiennent toutes dans ma tête. :)
cdleary
Je peux apprécier cela. Attention cependant, dans des cas comme celui-ci, vous avez une construction et une affectation d'objets supplémentaires qui ne sont pas nécessaires.
lu
Un autre bon point - se fier à l'opérateur d'affectation par défaut n'est pas une bonne pratique pour les exemples publics. ;)
cdleary

Réponses:

152

std::map's operator []n'est pas déclaré comme const, et ne peut pas être dû à son comportement:

T & opérateur [] (const Key & key)

Renvoie une référence à la valeur mappée à une clé équivalente à key, en effectuant une insertion si cette clé n'existe pas déjà.

Par conséquent, votre fonction ne peut pas être déclarée constet utiliser les fichiers operator[].

std::mapLafind() fonction de vous permet de rechercher une clé sans modifier la carte.

find()renvoie un iterator, ou const_iteratorà un std::paircontenant à la fois la clé ( .first) et la valeur ( .second).

Dans C ++ 11, vous pouvez également utiliser at()pour std::map. Si l'élément n'existe pas, la fonction lève une std::out_of_rangeexception, contrairement à operator [].

Luke
la source
8
en plus: VALUE = map.find (KEY) -> second; J'ai dû apprendre que 'find ()' renvoie un itérateur, qui est de type paire.
FlipMcF
5
J'ajouterais que maintenant en C11 vous pouvez utiliser: std :: map :: at (key) et éviter l'itérateur.
Juan Besa
3
Intéressant. Je pense que C ++ ferait la distinction entre lvalue operator[](par exemple foo[bar] = baz) et rvalue operator[](par exemple x = foo[bar]) - ce dernier pourrait certainement être const.
Claudiu
15

Comme operator[]n'a pas de surcharge qualifiée const, elle ne peut pas être utilisée en toute sécurité dans une fonction qualifiée const. Cela est probablement dû au fait que la surcharge actuelle a été générée dans le but à la fois de renvoyer et de définir des valeurs de clé.

Au lieu de cela, vous pouvez utiliser:

VALUE = map.find(KEY)->second;

ou, en C ++ 11, vous pouvez utiliser l' at()opérateur:

VALUE = map.at(KEY);
Richard
la source
map.find(KEY)->second; n'est pas sûr lorsque les valeurs de la carte sont des chaînes. Il a tendance à imprimer des déchets lorsque la clé n'est pas trouvée.
syam
1
C'est une réponse bien expliquée qui va au point. Hier, j'ai passé 2 heures à essayer de comprendre ce qui se passait dans un cas similaire. Pouvons-nous convenir que le message d'erreur est, au mieux, trompeur? Je pourrais être beaucoup plus clair s'il n'avait pas le mot «ceci» et faisait référence à la const-ness au lieu du qualificatif plus générique .
carnicer
11

Vous ne pouvez pas utiliser operator[]sur une carte telle constque cette méthode n'est pas constcar elle vous permet de modifier la carte (vous pouvez l'attribuer à _map[key]). Essayez findplutôt d' utiliser la méthode.

nlativy
la source
1
En guise d'explication: que doit faire l'opérateur de la carte [] si la clé n'existe pas? Si la carte est non-const, la clé est ajoutée avec la valeur construite par défaut. Si la carte est const, que peut-on renvoyer par l'opérateur []? Il n'y a aucune valeur à cette clé.
C'est une réponse bien expliquée qui va au point. Hier, j'ai passé 2 heures à essayer de comprendre ce qui se passait dans un cas similaire. Pouvons-nous convenir que le message d'erreur est, au mieux, trompeur? Je pourrais être beaucoup plus clair s'il n'avait pas le mot «ceci» et faisait référence à la const-ness au lieu du qualificatif plus générique .
carnicer
7

Certaines versions plus récentes des en-têtes GCC (4.1 et 4.2 sur ma machine) ont des fonctions membres non standard map :: at () qui sont déclarées const et lancent std :: out_of_range si la clé n'est pas dans la carte.

const mapped_type& at(const key_type& __k) const

À partir d'une référence dans le commentaire de la fonction, il semble que cela a été suggéré comme une nouvelle fonction membre dans la bibliothèque standard.

Cuisine Nathan
la source
Je suppose que c'est un peu bizarre. La fonction at fait partie de la norme à venir, mais je ne trouve aucun at () dans la norme actuelle.
Sebastian Mach
'at' fait partie de C ++ 11.
Étienne le
0

Tout d'abord, vous ne devez pas utiliser de symboles commençant par _ car ils sont réservés à l'implémentation du langage / au rédacteur du compilateur. Il serait très facile pour _map d'être une erreur de syntaxe sur le compilateur de quelqu'un, et vous n'auriez personne d'autre à blâmer que vous-même.

Si vous souhaitez utiliser un trait de soulignement, placez-le à la fin, pas au début. Vous avez probablement fait cette erreur parce que vous avez vu du code Microsoft le faire. Rappelez-vous, ils écrivent leur propre compilateur, afin qu'ils puissent s'en tirer. Même ainsi, c'est une mauvaise idée.

l'opérateur [] ne renvoie pas seulement une référence, il crée en fait l'entrée dans la carte. Donc, vous n'obtenez pas seulement un mappage, s'il n'y en a pas, vous en créez un. Ce n'est pas ce que vous vouliez.

Dov
la source
5
Votre argument _est tout simplement faux. Les identifiants commençant par deux traits de soulignement ( __example) ou les identifiants commençant par un trait de soulignement et une majuscule ( _Example) sont réservés. _examplen'est pas réservé.
Ethan