Можно ли указать значение по умолчанию std::map
operator[]
, если ключ не существует?
Std:: значение по умолчанию для карты
Ответ 1
Нет, нет. Самое простое решение - написать свою собственную бесплатную функцию шаблона для этого. Что-то вроде:
#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 );
}
Обновление С++ 11
Назначение: учет для общих ассоциативных контейнеров, а также необязательные параметры компаратора и распределителя.
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;
}
Ответ 2
Хотя это точно не отвечает на вопрос, я обошел проблему с кодом следующим образом:
struct IntDefaultedToMinusOne
{
int i = -1;
};
std::map<std::string, IntDefaultedToMinusOne > mymap;
Ответ 3
В стандарте С++ (23.3.1.2) указывается, что введенное значение по умолчанию сконфигурировано по умолчанию, поэтому map
сам по себе не обеспечивает способ его выполнения. Ваш выбор:
- Введите тип значения конструктор по умолчанию, который инициализирует его до требуемого значения, или
- Оберните карту в своем классе, которая предоставляет значение по умолчанию и реализует
operator[]
, чтобы вставить это значение по умолчанию.
Ответ 4
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
Невозможно указать значение по умолчанию - это всегда значение, построенное по умолчанию (конструктор нулевых параметров).
На самом деле operator[]
, вероятно, делает больше, чем вы ожидаете, как будто для данного ключа на карте не существует значения, оно будет вставлять новый со значением из конструктора по умолчанию.
Ответ 6
Более общая версия, поддержка С++ 98/03 и более контейнеров
Работает с универсальными ассоциативными контейнерами, единственным параметром шаблона является сам тип контейнера.
Поддерживаемые контейнеры: std::map
, std::multimap
, std::unordered_map
, std::unordered_multimap
, wxHashMap
, QMap
, QMultiMap
, QHash
, QMultiHash
и т.д.
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;
}
Использование:
std::map<int, std::string> t;
t[1] = "one";
string s = get_with_default(t, 2, "unknown");
Вот аналогичная реализация с использованием класса-оболочки, который больше похож на метод get()
типа dict
в 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);
}
Использование:
std::map<int, std::string> t;
t[1] = "one";
string s = wrap_map(t).get(2, "unknown");
Ответ 7
Значение инициализируется с использованием конструктора по умолчанию, как говорят другие ответы. Однако полезно добавить, что в случае простых типов (интегральные типы, такие как типы int, float, pointer или POD (планировать старые данные)), значения инициализируются нулями (или обнуляются инициализацией значения (что эффективно то же самое), в зависимости от того, какая версия С++ используется).
В любом случае, нижняя линия - это то, что карты с простыми типами будут ноль-инициализировать новые элементы автоматически. Поэтому в некоторых случаях нет необходимости беспокоиться о явном указании начального значения по умолчанию.
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
См. Содержит ли круглые скобки после того, как имя типа имеет значение с новым? для получения более подробной информации по этому вопросу.
Ответ 8
Возможно, вы можете предоставить пользовательский распределитель, который выделяет нужное значение по умолчанию.
template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;
Ответ 9
С++ 17 предоставляет try_emplace
, который делает именно это. Он берет ключ и список аргументов для конструктора значений и возвращает пару: a iterator
и a bool
.: http://en.cppreference.com/w/cpp/container/map/try_emplace