Existe-t-il un moyen de spécifier la valeur par défaut std::map
's operator[]
renvoie lorsqu'une clé n'existe pas?
Non, il n'y en a pas. La solution la plus simple consiste à écrire votre propre fonction de modèle gratuit pour ce faire. Quelque chose comme:
#include <string>
#include <map>
using namespace std;
template <typename K, typename V>
V GetWithDef(const std::map <K,V> & m, const K & key, const V & defval ) {
typename std::map<K,V>::const_iterator it = m.find( key );
if ( it == m.end() ) {
return defval;
}
else {
return it->second;
}
}
int main() {
map <string,int> x;
...
int i = GetWithDef( x, string("foo"), 42 );
}
Mise à jour C++ 11
Objectif: prendre en compte les conteneurs associatifs génériques, ainsi que les paramètres facultatifs de comparateur et d'allocateur.
template <template<class,class,class...> class C, typename K, typename V, typename... Args>
V GetWithDef(const C<K,V,Args...>& m, K const& key, const V & defval)
{
typename C<K,V,Args...>::const_iterator it = m.find( key );
if (it == m.end())
return defval;
return it->second;
}
Bien que cela ne réponde pas exactement à la question, j'ai contourné le problème avec un code comme celui-ci:
struct IntDefaultedToMinusOne
{
int i = -1;
};
std::map<std::string, IntDefaultedToMinusOne > mymap;
La norme C++ (23.3.1.2) spécifie que la nouvelle valeur insérée est construite par défaut, donc map
lui-même ne fournit pas de moyen de le faire. Vos choix sont:
operator[]
pour insérer cette valeur par défaut.Version plus générale, prise en charge de C++ 98/03 et plus de conteneurs
Fonctionne avec des conteneurs associatifs génériques, le seul paramètre de modèle est le type de conteneur lui-même.
Conteneurs pris en charge: std::map
, std::multimap
, std::unordered_map
, std::unordered_multimap
, wxHashMap
, QMap
, QMultiMap
, QHash
, QMultiHash
, etc.
template<typename MAP>
const typename MAP::mapped_type& get_with_default(const MAP& m,
const typename MAP::key_type& key,
const typename MAP::mapped_type& defval)
{
typename MAP::const_iterator it = m.find(key);
if (it == m.end())
return defval;
return it->second;
}
Usage:
std::map<int, std::string> t;
t[1] = "one";
string s = get_with_default(t, 2, "unknown");
Voici une implémentation similaire en utilisant une classe wrapper, qui est plus similaire à la méthode get()
de dict
type en Python: https://github.com/hltj/ wxMEdit/blob/master/src/xm/xm_utils.hpp
template<typename MAP>
struct map_wrapper
{
typedef typename MAP::key_type K;
typedef typename MAP::mapped_type V;
typedef typename MAP::const_iterator CIT;
map_wrapper(const MAP& m) :m_map(m) {}
const V& get(const K& key, const V& default_val) const
{
CIT it = m_map.find(key);
if (it == m_map.end())
return default_val;
return it->second;
}
private:
const MAP& m_map;
};
template<typename MAP>
map_wrapper<MAP> wrap_map(const MAP& m)
{
return map_wrapper<MAP>(m);
}
Usage:
std::map<int, std::string> t;
t[1] = "one";
string s = wrap_map(t).get(2, "unknown");
template<typename T, T X>
struct Default {
Default () : val(T(X)) {}
Default (T const & val) : val(val) {}
operator T & () { return val; }
operator T const & () const { return val; }
T val;
};
<...>
std::map<KeyType, Default<ValueType, DefaultValue> > mapping;
C++ 17 fournit try_emplace
qui fait exactement cela. Il prend une clé et une liste d'arguments pour le constructeur de valeurs et renvoie une paire: un iterator
et un bool
.: http://en.cppreference.com/w/ cpp/container/map/try_emplace
Il n'y a aucun moyen de spécifier la valeur par défaut - c'est toujours une valeur construite par défaut (constructeur de paramètre zéro).
En réalité operator[]
fait probablement plus que ce que vous attendez car si aucune valeur n'existe pour la clé donnée dans la carte, elle en insérera une nouvelle avec la valeur du constructeur par défaut.
La valeur est initialisée à l'aide du constructeur par défaut, comme le disent les autres réponses. Cependant, il est utile d'ajouter qu'en cas de types simples (types intégraux tels que les types int, float, pointer ou POD (plan old data)), les valeurs sont initialisées à zéro (ou à zéro par initialisation de valeur (ce qui est en fait la même chose), selon la version de C++ utilisée).
Quoi qu'il en soit, l'essentiel est que les cartes avec des types simples initialiseront automatiquement les nouveaux éléments à zéro. Dans certains cas, il n'est donc pas nécessaire de se soucier de spécifier explicitement la valeur initiale par défaut.
std::map<int, char*> map;
typedef char *P;
char *p = map[123],
*p1 = P(); // map uses the same construct inside, causes zero-initialization
assert(!p && !p1); // both will be 0
Voir Les parenthèses après le nom du type font-elles une différence avec new? pour plus de détails sur le sujet.
Vous pouvez peut-être donner un allocateur personnalisé qui alloue avec la valeur par défaut que vous souhaitez.
template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;
Une solution consiste à utiliser map::at()
au lieu de []
. Si une clé n'existe pas, at
lève une exception. Encore plus agréable, cela fonctionne également pour les vecteurs et convient donc à la programmation générique où vous pouvez échanger la carte avec un vecteur.
L'utilisation d'une valeur personnalisée pour une clé non enregistrée peut être dangereuse car cette valeur personnalisée (comme -1) peut être traitée plus loin dans le code. À quelques exceptions près, il est plus facile de repérer les bogues.