std::map valor predeterminado


¿Hay alguna forma de especificar el valor predeterminado std::map's operator[] devuelve cuando no existe una clave?

 64
Author: Sergey K., 2010-02-25

9 answers

No, no lo hay. La solución más simple es escribir su propia función de plantilla gratuita para hacer esto. Algo como:

#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 );
}

Actualización de C++11

Propósito: Tenga en cuenta los contenedores asociativos genéricos, así como los parámetros comparadores y asignadores opcionales.

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;
}
 38
Author: WhozCraig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-05 15:49:00

Aunque esto no responde exactamente a la pregunta, he eludido el problema con un código como este:

struct IntDefaultedToMinusOne
{
    int i = -1;
};

std::map<std::string, IntDefaultedToMinusOne > mymap;
 17
Author: SurvivalMachine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-11 08:32:59

El estándar de C++ (23.3.1.2) especifica que el valor recién insertado se construye por defecto, por lo que map no proporciona una forma de hacerlo. Sus opciones son:

  • Dé al tipo de valor un constructor predeterminado que lo inicialice al valor que desee, o
  • Envuelva el mapa en su propia clase que proporciona un valor predeterminado e implementa operator[] para insertar ese valor predeterminado.
 11
Author: Mike Seymour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-25 12:07:42
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;
 4
Author: Thomas Eding,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-08 17:49:00

Versión Más General, Soporte C++98/03 y Más Contenedores

Funciona con contenedores asociativos genéricos, el único parámetro de plantilla es el propio tipo de contenedor.

Admite contenedores: 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;
}

Uso:

std::map<int, std::string> t;
t[1] = "one";
string s = get_with_default(t, 2, "unknown");

Aquí hay una implementación similar mediante el uso de una clase wrapper, que es más similar al método get() de tipo dict 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);
}

Uso:

std::map<int, std::string> t;
t[1] = "one";
string s = wrap_map(t).get(2, "unknown");
 4
Author: jyw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-19 17:21:47

No hay forma de especificar el valor por defecto - siempre es valor construido por el valor por defecto (constructor de parámetro cero).

De hecho, operator[] probablemente hace más de lo que espera, ya que si no existe un valor para la clave dada en el mapa, insertará una nueva con el valor del constructor predeterminado.

 3
Author: Michael Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-25 17:46:59

C++17 proporciona try_emplace que hace exactamente esto. Toma una clave y una lista de argumentos para el constructor de valores y devuelve un par: an iterator y a bool.: http://en.cppreference.com/w/cpp/container/map/try_emplace

 3
Author: Ben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-10 17:22:32

El valor se inicializa usando el constructor predeterminado, como dicen las otras respuestas. Sin embargo, es útil agregar que en el caso de tipos simples (tipos integrales como int, float, pointer o POD (plan old data)), los valores se inicializan a cero (o se ponen a cero por inicialización de valor (que es efectivamente lo mismo), dependiendo de la versión de C++ que se use).

De todos modos, la línea de fondo es que los mapas con tipos simples inicializarán cero los nuevos elementos automáticamente. Así que en algunos casos, no hay necesidad de preocuparse por especificar explícitamente el valor inicial predeterminado.

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

Ver ¿Los paréntesis después del nombre del tipo hacen una diferencia con nuevo? para más detalles sobre el asunto.

 2
Author: the swine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:30

Tal vez usted puede dar un asignador personalizado que asignar con un valor predeterminado que desee.

template < class Key, class T, class Compare = less<Key>,
       class Allocator = allocator<pair<const Key,T> > > class map;
 1
Author: VDVLeon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-25 12:06:45