OVH Cloud OVH Cloud

std::map::operator[] const

16 réponses
Avatar
Mickaël Wolff
Bonjour,

J'ai cherché des justifications au fait que cette surcharge n'existe
pas. Et je n'en ai pas trouvé, hormis le fait que l'inexistance de la
clé entraîne l'ajout d'une paire dans la map.

Pourquoi ?
Existe-t-il une méthode élégante pour contourner ce manque ?
Peut-être que je me trompe dans mon interprétation de ce qu'est une
map, et que donc je n'utilises pas le bon outil ?

Merci :)
--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org

6 réponses

1 2
Avatar
Gabriel Dos Reis
Mickaël Wolff writes:

|
| > Peux-tu montrer le genre de codes typiques que tu écrirais avec ça ?
| > Pourquoi cela est-il meilleur qu'utiliser find() ou binary_search() ?
|
| Je l'utilisais par soucis de concision, pour rendre le code plus
| lisible.

Comme je l'avais suggéré plus haut, peux-tu montrer le genre de codes
typiques que tu écrirais pourqu'on puisse juger si cela serait plus
concis et plus lisible ?

[...]

| Le problème de find est qu'en fonction du conteneur, il ne renvoie pas
| la même chose. Un itérateur tantôt sur le type stocké (vector, deque),
| tantôt sur une paire (map), alors que operator[] renvoie toujour une
| référence sur le type.

le type stocké est donné par value_type -- donc, cela renvoie bien la
même chose pour vector, list, et map. Au contraire, c'est operator[]
qui ne renvoie pas toujours une référence sur value_type.

-- Gaby
Avatar
James Kanze
On Apr 22, 5:11 am, Fabien LE LEZ wrote:
On Tue, 22 Apr 2008 02:10:13 +0200, Mickaël Wolff :

Le problème de find est qu'en fonction du conteneur, il ne renvoie pas
la même chose.


C'est vrai que les interfaces des types de la STL ne sont pas toujours
idéales. Mais il faut considérer les conteneurs standard comme des
briques de base ; tu peux facilement les encapsuler dans tes propres
classes, pour fournir l'interface qui te convient.

alors que operator[] renvoie toujour une référence sur le type.


Attention, vector<>::operator[] ne lance pas non plus d'exception.


Il se présente un peu différement : il est assez facile de
démander d'abord si l'indice s'y trouve (« i < v.size() »). Du
coup, sinon, c'est un comportement indéfini, c-à-d un core dump
garanti dans toute implémentation qui se respecte.

En revanche, ça ne fait pas partie de la STL, mais on pourrait
bien imaginer un concepte « Indexable » pour une collection
qui 1) supporte l'opérateur [] (const et non const) et 2) a une
fonction supplémentaire « contains( index ) » ou « isValid(
index ) » ou quelque chose du genre. Si on admet que le
comportement de l'opérateur [] est indéfini si « !
contains() », on n'a besoin que d'ajouter contains() à std::map
et à std::vector pour qu'ils soient conforme tous les deux --
indéfini laissant la liberté de définir dans des cas
particulier. Et en étendant ce concepte : at() lève une
exception si l'indice n'est pas valide, et get() renvoie un
pointeur, avec un pointeur null si l'indice n'est pas valide.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Mickaël Wolff

Comme je l'avais suggéré plus haut, peux-tu montrer le genre de codes
typiques que tu écrirais pourqu'on puisse juger si cela serait plus
concis et plus lisible ?


class message
{
typedef std::map<std::string,std::string> map ;
public:
std::string & get_value(std::string key) const
throw(std::range_error)
{
// return m_value_list[key] ; // ça marche pas ©

map::const_iterator s = m_value_list.find(key) ;
if(s == m_value_list.end())
throw std::range_error("message::get_value()") ;

return s->second ;
}

protected:
map m_value_list ;
} ;


le type stocké est donné par value_type -- donc, cela renvoie bien la
même chose pour vector, list, et map. Au contraire, c'est operator[]
qui ne renvoie pas toujours une référence sur value_type.


Ben non :)

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org

Avatar
Gabriel Dos Reis
Mickaël Wolff writes:

|
| > Comme je l'avais suggéré plus haut, peux-tu montrer le genre de codes
| > typiques que tu écrirais pourqu'on puisse juger si cela serait plus
| > concis et plus lisible ?
|
| class message
| {
| typedef std::map<std::string,std::string> map ;
| public:
| std::string & get_value(std::string key) const
| throw(std::range_error)
| {
| // return m_value_list[key] ; // ça marche pas ©
|
| map::const_iterator s = m_value_list.find(key) ;
| if(s == m_value_list.end())
| throw std::range_error("message::get_value()") ;
|
| return s->second ;
| }
|
| protected:
| map m_value_list ;
| } ;

Ça c'est le code qui implémente l'équivalent de operator[] ; ce que je
demandait, c'est exemple d'utilisation.

|
|
| > le type stocké est donné par value_type -- donc, cela renvoie bien la
| > même chose pour vector, list, et map. Au contraire, c'est operator[]
| > qui ne renvoie pas toujours une référence sur value_type.
|
| Ben non :)

Comment ça non ?

-- Gaby
Avatar
James Kanze
On Apr 22, 8:15 pm, Gabriel Dos Reis wrote:
Mickaël Wolff writes:


Comme je l'avais suggéré plus haut, peux-tu montrer le
genre de codes typiques que tu écrirais pourqu'on puisse
juger si cela serait plus concis et plus lisible ?


class message
{
typedef std::map<std::string,std::string> map ;
public:
std::string & get_value(std::string key) const
throw(std::range_error)
{
// return m_value_list[key] ; // ça marche pas ©

map::const_iterator s = m_value_list.find(key) ;
if(s == m_value_list.end())
throw std::range_error("message::get_value()") ;

return s->second ;
}

protected:
map m_value_list ;
} ;


Ça c'est le code qui implémente l'équivalent de operator[] ;


Pas tout à fait, vue qu'il fonctionne sur un objet const, et
n'insère rien.

Comme il me semble avoir déjà dit, selon l'utilisation, le
comportement voulu dans l'absense d'une entrée varie. Dans un
certain sens, l'abstraction de base de std::map ne fait pas de
choix : elle donne les fonctions habituelles find, insert et
erase, pour que tu implémentes ce qu'il te faut au-dessus (comme
ici). AMHA, l'opérateur [] ne fait pas réelement partie de
l'abstraction de base de std::map ; il est présente plutôt par
commodité, pour permettre son utilisation comme un tableau AWK
ou perl, dans le cas où ça convient.

ce que je demandait, c'est exemple d'utilisation.


C'est un bon exemple d'une utilisation typique de l'abstraction
de base, qui est, à mon avis, conçue précisement pour supporter
ce genre d'utilisation.

le type stocké est donné par value_type -- donc, cela
renvoie bien la même chose pour vector, list, et map. Au
contraire, c'est operator[] qui ne renvoie pas toujours
une référence sur value_type.


Ben non :)


Comment ça non ?


Le problème, je crois, c'est que la STL considère std::map comme
une collection, or que la plupart du temps, on l'utilise comme
une application (au sens mathématique). Et dans ces
utilisations, « value_type » a une autre signification ;
pense à « attribute-value pair », un concepte bien connu. À
cet égard, la bibliothèque Java, qui distingue entre le concepte
de collection et celui de map, correspond plus aux utilisations
courantes. Ceci dit, Java fournit aussi un type Map::Entry
comme partie de l'interface contractuelle, avec aussi la
possibilité d'itérer sur ces entrées. C-à-d que pragmatiquement,
il doit y avoir aussi des cas où on veut bien le considérer
comme une collection (ce que Java résoud par des « view »).

Peut-être ce qui manque à la STL, ici, c'est ce concepte de
« map » (ou « indexable », si on veut y inclure
std::vector). En attendant, en revanche, il n'est pas trop
difficile à implémenter soi-même, sur les bases qu'on a.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
Gabriel Dos Reis
James Kanze writes:

| On Apr 22, 8:15 pm, Gabriel Dos Reis wrote:
| > Mickaël Wolff writes:
|
|
| > > > Comme je l'avais suggéré plus haut, peux-tu montrer le
| > > > genre de codes typiques que tu écrirais pourqu'on puisse
| > > > juger si cela serait plus concis et plus lisible ?
|
| > > class message
| > > {
| > > typedef std::map<std::string,std::string> map ;
| > > public:
| > > std::string & get_value(std::string key) const
| > > throw(std::range_error)
| > > {
| > > // return m_value_list[key] ; // ça marche pas ©
| > >
| > > map::const_iterator s = m_value_list.find(key) ;
| > > if(s == m_value_list.end())
| > > throw std::range_error("message::get_value()") ;
| > >
| > > return s->second ;
| > > }
|
| > > protected:
| > > map m_value_list ;
| > > } ;
|
| > Ça c'est le code qui implémente l'équivalent de operator[] ;
|
| Pas tout à fait, vue qu'il fonctionne sur un objet const, et
| n'insère rien.

Il voulait un operator[] qui n'insère pas, mais lève une exception
pour un object const. N'est-ce pas ce que le code ci-dessus ?
Ma question est comment utilise-t-il operator[] (ou getValue())
typiquement pour si cela ressemble à l'utilisation tyoique
de vector<>::operator[].


| Comme il me semble avoir déjà dit, selon l'utilisation, le
| comportement voulu dans l'absense d'une entrée varie. Dans un
| certain sens, l'abstraction de base de std::map ne fait pas de
| choix : elle donne les fonctions habituelles find, insert et
| erase, pour que tu implémentes ce qu'il te faut au-dessus (comme
| ici).

J'ai bien compris cela, merci. Ma question est différente.

De mon point de vue, pour arguer que operator[] doit exister dans la
STL (qui rappelonsl-e, au cas où, fournit un cadre *générique*)
et renvoyer une exception, je voudrais voir du code pour comparer le
style d'utilisation et ce qu'il a montré ci-dessus ne correspond pas à
ce je demandais.

Par example prend la fonction at(). Elle a été ajoutée, mais si tu
regardes bien son utilisation typique reseemble à ce qu'on fait avec
vector

try {
// attempt at()
}
catch(const out_of_range&) {
// oops someone wasn't paying attention.
}

De ce point de vue là, il devient tenable d'arguer de similarité.


Mais si tu *ne peux pas* montrer du code typique pour les utilisations
sont similaires, l'argument qu'il faudrait ajouter operator[] parce
qu'un autre conteneur l'a est faible.


| AMHA, l'opérateur [] ne fait pas réelement partie de
| l'abstraction de base de std::map ; il est présente plutôt par
| commodité, pour permettre son utilisation comme un tableau AWK
| ou perl, dans le cas où ça convient.

Ça c'est du blah blah blah, je veux voir du code d'utilisation pour juger.


[...]

| > ce que je demandait, c'est exemple d'utilisation.
|
| C'est un bon exemple d'une utilisation typique de l'abstraction
| de base, qui est, à mon avis, conçue précisement pour supporter
| ce genre d'utilisation.

Non, désolé.

| > > > le type stocké est donné par value_type -- donc, cela
| > > > renvoie bien la même chose pour vector, list, et map. Au
| > > > contraire, c'est operator[] qui ne renvoie pas toujours
| > > > une référence sur value_type.
|
| > > Ben non :)
|
| > Comment ça non ?
|
| Le problème,

map<Key,T>::value_type , c'est une pair<const Key,T>, et
map<Key,T>::operator[] renvoie T&. vector<T>::value_type c'est T,
et vector<T>::operator[] renvoie une référence (const ou non) sur T.
Si tu affirmes « ben non », alors tu dois utiliser quelque chose qui
n'est pas C++.

-- Gaby
1 2