Есть ли способ указать значение по умолчанию std::map
operator[]
возвращается, когда ключ не существует?std :: map default value
ответ
Нет, не существует. Самое простое решение - написать свою собственную бесплатную функцию шаблона для этого. Что-то вроде:
#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);
}
C++ 11 Обновление
Цель: Счет для общих ассоциативных контейнеров, а также дополнительных компараторов и Allocator параметров.
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;
}
Хорошее решение. Возможно, вы захотите добавить несколько аргументов шаблона, чтобы шаблон функции работал с картами, которые не используют параметры шаблона по умолчанию для компаратора и распределителя. – sbi
+1, но для обеспечения того же поведения, что и 'operator []' со значением по умолчанию, значение по умолчанию должно быть вставлено в карту внутри 'if (it == m.end())' block –
@David Я предполагаю, что OP на самом деле не хочет этого поведения. Я использую аналогичную схему для чтения конфигураций, но я не хочу, чтобы конфигурация обновлялась, если отсутствует ключ. – 2010-02-25 12:22:20
Невозможно указать значение по умолчанию - это всегда значение, построенное по умолчанию (конструктор нулевых параметров).
Фактически operator[]
, вероятно, делает больше, чем вы ожидаете, как будто для данного ключа на карте не существует значения, которое будет вставлять новый со значением из конструктора по умолчанию.
Правильно, чтобы избежать добавления новых записей, вы можете использовать' find', который возвращает конечный итератор, если для данного ключа не существует элемента. –
Возможно, вы можете предоставить пользовательский распределитель, который выделяет нужное по умолчанию значение.
template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;
'operator []' возвращает объект, созданный при вызове 'T()', независимо от того, что делает распределитель. – sbi
@sbi: Не отображает ли карта метод 'construct' распределителей? Думаю, изменить это можно. Я подозреваю, что функция 'construct', которая не что-то отличается от' new (p) T (t); 'не является корректной. EDIT: Оглядываясь назад, это было глупо, иначе все значения были бы одинаковыми: P Где мой кофе ... – GManNickG
@GMan: моя копия C++ 03 говорит (в 23.3.1.2), что 'operator []' возвращает ' (* ((insert (make_pair (x, T()))) сначала)). second'. Поэтому, если я ничего не пропущу, этот ответ неверен. – sbi
C++ стандарт (23.3.1.2) указывает, что вновь вставленный значение по умолчанию строится, поэтому map
сама по себе не обеспечивает способ сделать это. Ваш выбор:
- Дайте значение типа конструктора по умолчанию, который инициализирует его значение, которое вы хотите, или
- Оберните карту в своем собственном классе, который обеспечивает значение по умолчанию и реализует
operator[]
для вставки этого по умолчанию.
Ну, точнее, вновь вставленное значение инициализируется значением (8.5.5) так: - если T - тип класса с объявленным пользователем конструктором (12.1), то конструктор по умолчанию для T называется (и инициализация плохо сформирована, если T не имеет доступного конструктора по умолчанию); - если T - тип неединичного класса без конструктора, объявленного пользователем, то каждый нестатический элемент данных и базовый класс компонент T инициализируется значением; - если T - тип массива, то каждый элемент инициализируется значением; - в противном случае объект инициализируется нулем –
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;
Попробуйте со строкой и буквальным. это не работает. –
Затем измените его, чтобы он работал. Я не собираюсь исправлять случаи, когда этот код не был предназначен для выполнения. –
Значение инициализируется с помощью конструктора по умолчанию, как говорят другие ответы. Однако полезно добавить, что в случае простых типов (интегральные типы, такие как типы int, float, pointer или POD (планировать старые данные)), значения инициализируются нулями (или обнуляются инициализацией значения (что эффективно то же самое), в зависимости от того, какая версия C++ используется).
В любом случае, нижняя линия - это то, что карты с простыми типами будут ноль-инициализировать новые элементы автоматически. Поэтому в некоторых случаях нет необходимости беспокоиться о явном определении начального значения по умолчанию.
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
См Do the parentheses after the type name make a difference with new? для получения более подробной информации по данному вопросу.
более общий вариант, Поддержка C++ 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");
Хотя это не точно ответить на этот вопрос, я обойти эту проблему с кодом, как это:
struct IntDefaultedToMinusOne
{
int i = -1;
};
std::map<std::string, IntDefaultedToMinusOne > mymap;
C++ 17 обеспечивает try_emplace
, который делает именно это. Он принимает ключ и список аргументов для конструктора значения и возвращает пару: iterator
и bool
:. http://en.cppreference.com/w/cpp/container/map/try_emplace
эта общая конструкция очень шикарно в Perl: 'моего $ Вэл = $ отображения {«ключ» }: «NAN» ' –