std :: map默认值

ano*_*non 76 c++ stdmap

当密钥不存在时,有没有办法指定默认值std::mapoperator[]返回值?

小智 44

不,没有.最简单的解决方案是编写自己的免费模板函数来执行此操作.就像是:

#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 );
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

  • @David我假设OP实际上并不想要这种行为.我使用类似的方案来读取配置,但如果缺少密钥,我不希望更新配置. (12认同)
  • 1,而是提供的确切相同的行为`操作符[]`具有默认值,默认值应插入到`如果(它== m.end())`块内的地图 (3认同)
  • @GMan bool参数被一些人认为是不好的风格,因为你不能通过查看调用(而不是声明)来判断他们做什么 - 在这种情况下,"true"意味着"使用默认"或"不要"使用默认"(或完全不同的东西)?枚举总是更清晰,但当然是更多的代码.我自己对这个问题有两种想法. (2认同)
  • 如果默认值为nullptr,则此答案不起作用,但http://stackoverflow.com/a/26958878/297451可以. (2认同)

Sur*_*ine 30

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

struct IntDefaultedToMinusOne
{
    int i = -1;
};

std::map<std::string, IntDefaultedToMinusOne > mymap;
Run Code Online (Sandbox Code Playgroud)

  • 这对我来说是最好的解决方案。易于实现,非常灵活且通用。 (3认同)
  • 请注意,您可以添加“operator int() const {return i;}”,这允许您在大多数情况下将其视为“int”。 (3认同)
  • @acegs你可以让它实际上是通用的:`template &lt;typename T&gt; struct default { T t; };` 那么你可以考虑添加转换运算符和转换构造函数 (2认同)

Ben*_*Ben 12

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

  • @Ben:恕我直言,OP实际上并不打算修改地图,而是拥有相当于 python Dict get(key,defaultValue) 的内容。std 中肯定缺少 API 来为这种情况提供函数重载 (4认同)
  • 不是吗?问题是“有没有办法指定当键不存在时返回的默认值‘std::map’的‘operator[]’?” 这意味着他们正在调用“operator[]”的非“const”版本,它(可能)会修改映射。 (2认同)

Mik*_*our 11

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

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

  • 好吧,确切地说,新插入的值是值初始化(8.5.5)所以: - 如果T是具有用户声明的构造函数(12.1)的类类型,则调用T的默认构造函数(并且初始化生病了-form如果T没有可访问的默认构造函数); - 如果T是没有用户声明的构造函数的非联合类类型,则T的每个非静态数据成员和基类组件都是值初始化的; - 如果T是数组类型,则每个元素都是值初始化的; - 否则,对象被零初始化 (7认同)

jyw*_*jyw 6

更多通用版本,支持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;
}
Run Code Online (Sandbox Code Playgroud)

用法:

std::map<int, std::string> t;
t[1] = "one";
string s = get_with_default(t, 2, "unknown");
Run Code Online (Sandbox Code Playgroud)

下面是一个使用包装类的类似实现,它类似于Python中get()dict类型方法: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);
}
Run Code Online (Sandbox Code Playgroud)

用法:

std::map<int, std::string> t;
t[1] = "one";
string s = wrap_map(t).get(2, "unknown");
Run Code Online (Sandbox Code Playgroud)

  • MAP::mapped_type&amp; 返回不安全,因为 typename MAP::mapped_type&amp; defval 可能超出范围。 (2认同)

Mic*_*son 5

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

事实上,operator[]可能比您预期的要多,就好像映射中给定键的值不存在一样,它会插入一个具有默认构造函数值的新值。

  • 是的,为了避免添加新条目,您可以使用 `find`,如果给定键不存在元素,它会返回结束迭代器。 (2认同)

Tho*_*ing 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;
Run Code Online (Sandbox Code Playgroud)

  • 然后对其进行修改,使其起作用。我不会费心去解决这种代码并非旨在解决的情况。 (3认同)

the*_*ine 5

正如其他答案所说,该值是使用默认构造函数初始化的。但是,在简单类型(整数类型,如 int、float、指针或 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
Run Code Online (Sandbox Code Playgroud)

请参阅类型名称后的括号是否与 new 有所不同?有关此事的更多详细信息。