Pourquoi l'opérateur [] n'est-il pas const pour les cartes STL?

89

Exemple artificiel, pour le bien de la question:

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map[x] << std::endl
}

Cela ne compilera pas, car l'opérateur [] est non-const.

C'est dommage, car la syntaxe [] a l'air très propre. Au lieu de cela, je dois faire quelque chose comme ceci:

void MyClass::MyFunction( int x ) const
{
  MyMap iter = m_map.find(x);
  std::cout << iter->second << std::endl
}

Cela m'a toujours dérangé. Pourquoi l'opérateur [] est-il non-const?

Runcible
la source
5
Que devrait operator[]rapporter au cas où l'élément donné n'existe pas?
Frerich Raabe
4
@Frerich Raabe: La même chose que la fonction membre at: throw std :: out_of_range
Jean-Simon Brochu

Réponses:

90

Pour std::mapet std::unordered_map, operator[]insérera la valeur d'index dans le conteneur si elle n'existait pas auparavant. C'est un peu peu intuitif, mais c'est comme ça.

Puisqu'il doit être autorisé à échouer et à insérer une valeur par défaut, l'opérateur ne peut pas être utilisé sur une constinstance du conteneur.

http://en.cppreference.com/w/cpp/container/map/operator_at

Alan
la source
3
std::setn'a pas operator[].
avakar le
2
C'est la bonne réponse, mais une version const pourrait faire la même chose que le membre "at". C'est jeter un std :: out_of_range ...
Jean-Simon Brochu
Lorsqu'il est utilisé pour lire une valeur, il n'y a pas de valeur par défaut à fournir. std::vectora un opérateur de lecture []qui est const. mapdevrait faire de même.
wcochran le
51

Maintenant qu'avec C ++ 11, vous pouvez avoir une version plus propre en utilisant at ()

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map.at(x) << std::endl;
}
Deqing
la source
4
Si mapont const et non-const at()s - pourquoi pas la même chose aussi pour operator[]? avec la version const n'insérant rien mais plutôt jetant? (Ou renvoyer un optionnel, lorsque std :: optional en fait le standard)
einpoklum
@einpoklum Le point de correction de const est principalement la vérification statique à la compilation. Je préfère que le compilateur se plaigne plutôt que de lancer une exception car je n'ai pas utilisé correctement les objets const.
Millie Smith
@einpoklum Très tard, mais pour les autres lecteurs: avoir deux surcharges faisant des choses aussi différentes serait terrible. La seule raison atexiste en deux versions, c'est parce qu'il fait un return *this;, et la seule différence entre les surcharges est la const-ness de la référence renvoyée. Les effets réels des deux ats sont exactement les mêmes (c'est-à-dire aucun effet).
HTNW
28

Remarque pour les nouveaux lecteurs.
La question d'origine concernait les conteneurs STL (pas spécifiquement le std :: map)

Il faut noter qu'il existe une version const de operator [] sur la plupart des conteneurs.
C'est juste que std :: map et std :: set n'ont pas de version const et c'est le résultat de la structure sous-jacente qui les implémente.

De std :: vector

reference       operator[](size_type n) 
const_reference operator[](size_type n) const 

Pour votre deuxième exemple, vous devez également vérifier si l'élément n'a pas été trouvé.

void MyClass::MyFunction( int x ) const
{
    MyMap iter = m_map.find(x);
    if (iter != m_map.end())
    {
        std::cout << iter->second << std::endl
    }
}
Martin York
la source
1
std::setn'a pas operator[]du tout.
Tout le monde le
2

Puisque l'opérateur [] peut insérer un nouvel élément dans le conteneur, il ne peut pas s'agir d'une fonction membre const. Notez que la définition de l'opérateur [] est extrêmement simple: m [k] équivaut à (* ((m.insert (value_type (k, data_type ()))). First)). Second. À proprement parler, cette fonction membre n'est pas nécessaire: elle n'existe que par commodité

Satbir
la source
0

Un opérateur d'index ne doit être const que pour un conteneur en lecture seule (qui n'existe pas vraiment en STL en soi).

Les opérateurs d'index ne sont pas seulement utilisés pour regarder des valeurs.

Nick Bedford
la source
6
La question est, pourquoi n'a-t-il pas deux versions surchargées - une const, une autre non const- comme par exemple std::vector.
Pavel Minaev
-2

Si vous déclarez que votre variable membre std :: map est mutable

mutable std::map<...> m_map;

vous pouvez utiliser les fonctions membres non const de std :: map dans vos fonctions membres const.

Anthony Cramp
la source
15
C'est une idée terrible, cependant.
GManNickG
7
L'API de votre classe se trouve si vous faites cela. La fonction prétend qu'elle est const - ce qui signifie qu'elle ne modifiera aucune variable membre - mais en réalité, elle pourrait modifier le membre de données m_map.
Runcible
2
mutablepeut être utilisé pour les membres comme std::mutex, les caches et les aides au débogage. Si la carte doit être utilisée comme cache pour accélérer une constfonction "getter" très coûteuse , alors mutablec'est acceptable. Vous devez être prudent, mais ce n'est pas une mauvaise idée en soi.
Mark Lakata