c++ - mapa - std:: map cplusplus
std:: map default value (9)
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: un iterator
y un bool
.: http://en.cppreference.com/w/cpp/container/map/try_emplace
¿Hay alguna manera de especificar el valor predeterminado que devuelve el operator[]
std::map
operator[]
cuando una clave no existe?
El estándar de C ++ (23.3.1.2) especifica que el nuevo valor insertado se construye por defecto, por lo que el map
sí no proporciona una forma de hacerlo. Sus opciones son:
- Asigne al tipo de valor un constructor predeterminado que lo inicialice al valor que desee, o
- Ajuste el mapa en su propia clase que proporciona un valor predeterminado e implementa el
operator[]
para insertar ese valor predeterminado.
El valor se inicializa utilizando el constructor predeterminado, como dicen las otras respuestas. Sin embargo, es útil agregar que en el caso de los tipos simples (tipos integrales como int, float, puntero o POD (datos antiguos planificados)), los valores se inicializan con cero (o se ponen a cero mediante la inicialización de valores (que es efectiva). lo mismo), dependiendo de la versión de C ++ que se use).
De todos modos, la conclusión es que los mapas con tipos simples inicializarán en cero los nuevos elementos automáticamente. Entonces, en algunos casos, no es necesario 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
Consulte ¿Los paréntesis después del nombre de tipo marcan la diferencia con el nuevo? Para más detalles al respecto.
No hay forma de especificar el valor predeterminado; siempre es un valor construido por el valor predeterminado (constructor de parámetro cero).
De hecho, el operator[]
probablemente haga más de lo esperado, ya que si no existiera un valor para la clave dada en el mapa, insertará uno nuevo con el valor del constructor predeterminado.
No, no hay. La solución más sencilla 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: Cuenta para los contenedores asociativos genéricos, así como los parámetros opcionales de comparador y asignador.
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;
}
Si bien esto no responde exactamente a la pregunta, he evitado el problema con un código como este:
struct IntDefaultedToMinusOne
{
int i = -1;
};
std::map<std::string, IntDefaultedToMinusOne > mymap;
Tal vez puede dar un asignador personalizado que asigne un valor predeterminado que desee.
template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;
Más versión 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.
Contenedores admitidos: std::map
, std::multimap
, std::unordered_map
, std::unordered_multimap
wxHashMap
, QMap
, QMultiMap
, QHash
, QMultiHash
, 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 utilizando una clase contenedora, que es más similar al método get()
del 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");
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;