web-dev-qa-db-fra.com

std :: map valeur par défaut

Existe-t-il un moyen de spécifier la valeur par défaut std::map's operator[] renvoie lorsqu'une clé n'existe pas?

74
anon

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;
}
43
anon

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;
28
SurvivalMachine

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:

  • Donnez au type de valeur un constructeur par défaut qui l'initialise à la valeur souhaitée, ou
  • Enveloppez la carte dans votre propre classe qui fournit une valeur par défaut et implémente operator[] pour insérer cette valeur par défaut.
11
Mike Seymour

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");
5
jyw
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;
5
Thomas Eding

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

4
Ben

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.

4
Michael Anderson

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.

2
the swine

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;
1
VDVLeon

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.

1
Dean