std :: map默认值

时间:2010-02-25 11:57:08

标签: c++ stdmap

当密钥不存在时,有没有办法指定默认值std::map的{​​{1}}返回?

13 个答案:

答案 0 :(得分:42)

不,没有。最简单的解决方案是编写自己的免费模板函数来执行此操作。类似的东西:

#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更新

目的:考虑通用关联容器,以及可选的比较器和分配器参数。

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 :(得分:24)

虽然这并没有完全回答这个问题,但我已经用这样的代码规避了这个问题:

struct IntDefaultedToMinusOne
{
    int i = -1;
};

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

答案 2 :(得分:11)

C ++标准(23.3.1.2)指定新插入的值是默认构造的,因此map本身不提供这样做的方法。您的选择是:

  • 为值类型提供一个默认构造函数,将其初始化为您想要的值,或
  • 将地图包装在您自己的类中,该类提供默认值并实现operator[]以插入该默认值。

答案 3 :(得分:5)

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 :(得分:5)

更多常规版本,支持C ++ 98/03和更多容器

使用通用关联容器,唯一的模板参数是容器类型本身。

支持的容器:std::mapstd::multimapstd::unordered_mapstd::unordered_multimapwxHashMapQMapQMultiMap,{{ 1}},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;
}

以下是使用包装类的类似实现,它类似于Python中std::map<int, std::string> t; t[1] = "one"; string s = get_with_default(t, 2, "unknown"); 类型的方法get()https://github.com/hltj/wxMEdit/blob/master/src/xm/xm_utils.hpp

dict

用法:

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

答案 5 :(得分:4)

无法指定默认值 - 它始终由默认值(零参数构造函数)构造。

实际上operator[]可能比你预期的更多,好像地图中给定键的值不存在,它会插入一个带有默认构造函数值的新值。

答案 6 :(得分:4)

C ++ 17提供了try_emplace,它正是这样做的。它接受值构造函数的键和参数列表,并返回一对:iteratorboolhttp://en.cppreference.com/w/cpp/container/map/try_emplace

答案 7 :(得分:2)

正如其他答案所说,使用默认构造函数初始化该值。但是,在简单类型(整数类型,如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?

答案 8 :(得分:2)

一种解决方法是使用map::at()而不是[]。 如果键不存在,则at会引发异常。 更好的是,它也适用于矢量,因此适用于可以将地图与矢量交换的通用编程。

使用未注册密钥的自定义值可能很危险,因为该自定义值(如-1)可能会在代码中进一步处理。除了例外,发现错误更容易。

答案 9 :(得分:1)

也许您可以为自定义分配器分配一个您想要的默认值。

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

答案 10 :(得分:1)

扩展答案https://stackoverflow.com/a/2333816/272642,此模板函数使用std::map的{​​{1}}和key_type typedef来推断mapped_type和{{1 }}。 没有这些typedef的容器就无法使用。

key

这使您可以使用

def

无需强制转换template <typename C> typename C::mapped_type getWithDefault(const C& m, const typename C::key_type& key, const typename C::mapped_type& def) { typename C::const_iterator it = m.find(key); if (it == m.end()) return def; return it->second; } 之类的参数。

答案 11 :(得分:1)

如果你能接触到C++17,我的解决方法如下:

std::map<std::string, std::optional<int>> myNullables;
std::cout << myNullables["empty-key"].value_or(-1) << std::endl;

这允许您在每次使用地图时指定一个“默认值”。这可能不一定是您想要或需要的,但为了完整起见,我将其发布在这里。这个解决方案非常适合函数范式,因为地图(和字典)通常以这种风格使用:

Map<String, int> myNullables;
print(myNullables["empty-key"] ?? -1);

答案 12 :(得分:0)

使用std::map::insert()

意识到我参加这个聚会已经很晚了,但是如果您对使用自定义默认值的operator[]的行为感兴趣(也就是说,找到没有给定键的元素) 插入映射中具有选定默认值的元素,并返回对新插入的值或现有值的引用),在C ++ 17之前的版本中已经有可用的函数供您使用:{{ 1}}。如果密钥已经存在,std::map::insert()实际上不会插入,而是将迭代器返回到现有值。

说,您想要一个字符串到整数的映射,如果还不存在该键,则插入默认值42:

insert

应输出42、43和44。

如果构造映射值的成本很高(如果复制/移动键或值类型的成本很高),则这将导致明显的性能损失,我认为可以使用C ++ 17的{{ 1}}。