2010-02-25 78 views
52

有沒有辦法指定默認值std::mapoperator[]當密鑰不存在時返回?std :: map默認值

+0

這個共同的結構是用Perl很優雅: '我的$ VAL = $地圖{「鑰匙」 }:「NAN」' – 2017-12-08 23:27:09

回答

34

不,沒有。最簡單的解決方案是編寫自己的免費模板功能來完成此操作。喜歡的東西:

​​

C++ 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; 
} 
+1

尼斯解決方案。您可能需要添加一些模板參數,以便該函數模板可以處理不使用比較器和分配器的默認模板參數的映射。 – sbi 2010-02-25 12:19:22

+3

+1,但爲了提供與'operator []'具有默認值完全相同的行爲,缺省值應插入到'if(it == m.end())'塊內的映射中 – 2010-02-25 12:19:24

+12

@David我假設OP實際上並不需要這種行爲。我使用類似的方案來讀取配置,但我不希望在缺少密鑰的情況下更新配置。 – 2010-02-25 12:22:20

3

沒有辦法指定默認值 - 它始終是由默認值(零參數構造函數)構造的值。

事實上,operator[]可能會做得比您預期的要多,就好像某個值不存在於映射中的給定鍵中,它會使用默認構造函數中的值插入一個新值。

+2

權,以避免增加新的項目,你可以使用'find'如果沒有元素存在一個給定的鍵,其不會返回結束迭代。 – 2010-02-25 12:05:16

1

也許你可以給一個自定義的分配器分配一個你想要的默認值。

template < class Key, class T, class Compare = less<Key>, 
     class Allocator = allocator<pair<const Key,T> > > class map; 
+4

'operator []'返回一個通過調用'T()'創建的對象,無論分配器做什麼。 – sbi 2010-02-25 12:17:09

+1

@sbi:映射不會調用allocator的'construct'方法嗎?我想可以改變這一點。儘管如此,我懷疑一個「構造」函數除了「new(p)T(t)」外沒有其他東西。編輯:在事後,這是愚蠢的,否則所有的值將是相同的:P哪裏是我的咖啡... – GManNickG 2010-02-25 16:04:25

+1

@GMan:我的C++ 03的副本說(在23.3.1.2)該經營者'[]''回報(*((insert(make_pair(x,T())))。first))。second'。所以除非我錯過了一些東西,否則這個答案是錯誤的。 – sbi 2010-02-26 12:44:28

10

C++標準(23.3.1.2)指定新插入的值默認構造,所以map本身並不提供這樣做的一種方式。你的選擇是:

  • 給值類型的默認構造函數,它初始化到你想要的值,或
  • 裹在自己的類地圖,提供了一個默認值,並實現operator[]以插入默認。
+6

那麼,要精確新插入的值的值初始化(8.5.5),因此: - 如果T是與用戶聲明的構造(12.1),然後對T中的默認構造函數被調用 類類型(和如果T沒有可訪問的默認構造函數,則初始化不合格); - 如果T是沒有一個用戶聲明的構造的非聯合類類型,那麼每一個非靜態數據成員和基類 的T分量是值初始化; - 如果T是一個數組類型,則每個元素都進行了值初始化; - 否則,該對象是零初始化的 – 2010-02-25 12:28:48

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; 
+0

嘗試使用字符串和文字。它不起作用。 – 2016-12-02 16:32:37

+2

然後修改它,使其工作。我不打算去修復這個代碼沒有設計完成的情況。 – 2016-12-02 21:16:10

2

值是使用默認構造函數初始化,如其他答案說。然而,如果添加簡單類型(int,float,pointer或POD(plan舊數據)類型等整型),則這些值是零初始化的(或者通過值初始化(這是有效的同樣的事情),取決於使用哪個版本的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?

4

更一般版本,支持C++ 98/03和多個容器

作品與通用關聯容器,唯一的模板的參數是容器類型本身。

支持容器:std::mapstd::multimapstd::unordered_mapstd::unordered_multimapwxHashMapQMapQMultiMapQHashQMultiHash

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"); 

這裏是一個類似的實現通過使用一個包裝類,這是更相似於方法dict類型的get()在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"); 
13

雖然這並不完全回答這個問題,我與代碼規避這個問題是這樣的:

struct IntDefaultedToMinusOne 
{ 
    int i = -1; 
}; 

std::map<std::string, IntDefaultedToMinusOne > mymap;