10 题: std :: map默认值

在...创建的问题 Fri, Feb 14, 2014 12:00 AM

有没有办法指定默认值std::map operator[]在一个键不存在时返回?

    
73
  1. 这个常见的构造在perl中非常优雅:my $val= $map{"key"} : "NAN"
    2017-12-08 23:27:09Z
  2. 醇>
    10个答案                              10 跨度>                         

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

     
    #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;
    }
    
        
    42
    2014-03-05 15:49:00Z
    1. 很好的解决方案。您可能希望添加一些模板参数,以便函数模板可以使用不使用比较器和分配器的默认模板参数的映射。
      2010-02-25 12:19:22Z
    2. + 1,但为了提供与operator[]完全相同的行为,默认值,应将默认值插入if ( it == m.end() )块内的地图
      2010-02-25 12:19:24Z
    3. @ David我假设OP实际上并不想要这种行为。我使用类似的方案来读取配置,但如果缺少密钥,我不希望更新配置。
      2010-02-25 12:22:20Z
    4. @ GMan bool参数被一些人认为是不好的风格,因为你不能通过查看调用(而不是声明)来判断他们做什么 - 在这种情况“真”是指“使用默认值”还是“不使用默认值”(或其他完全不同的东西)?枚举总是更清晰,但当然是更多的代码。我自己也有两个想法。
      2010-02-25 18:48:27Z
    5. 如果默认值为nullptr,则此答案无效,但 stackoverflow .com /a /26958878/297451
      2016-02-09 05:02:50Z
    6. 醇>

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

     
    struct IntDefaultedToMinusOne
    {
        int i = -1;
    };
    
    std::map<std::string, IntDefaultedToMinusOne > mymap;
    
        
    25
    2015-04-11 08:32:59Z
    1. 这对我来说是最好的解决方案。易于实施,非常灵活,通用。
      2019-04-08 01:36:59Z
    2. 醇>

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

    • 为值类型提供一个默认构造函数,将其初始化为您想要的值,或
    • 将地图包装在您自己的类中,该类提供默认值并实现operator[]以插入该默认值。
    11
    2010-02-25 12:07:42Z
    1. 确切地说,新插入的值是值初始化(8.5.5)所以: - 如果T是具有用户声明的构造函数的类类型(12.1) ,然后调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的); - 如果T是没有用户声明的构造函数的非联合类类型,则T的每个非静态数据成员和基类组件都是值初始化的; - 如果T是数组类型,则每个元素都是值初始化的; - 否则,该对象为零初始化
      2010-02-25 12:28:48Z
    2. 醇>
     
    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
    2012-08-08 17:49:00Z
    1. 尝试使用string和literal。它不起作用。
      2016-12-02 16:32:37Z
    2. 然后修改它以使其正常工作。我不打算修复这个代码不能完成的案例。
      2016-12-02 21:16:10Z
    3. 醇>

    更多常规版本,支持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");
    

    这是一个使用包装类的类似实现,它更类似于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);
    }
    

    用法:

     
    std::map<int, std::string> t;
    t[1] = "one";
    string s = wrap_map(t).get(2, "unknown");
    
        
    5
    2014-11-19 17:21:47Z

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

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

        
    4
    2012-07-25 17:46:59Z
    1. 是的,为了避免添加新条目,您可以使用find,如果给定键不存在任何元素,它会返回结束迭代器。
      2010-02-25 12:05:16Z
    2. 醇>

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

        
    4
    2017-07-10 17:22:32Z

    正如其他答案所说,使用默认构造函数初始化该值。但是,在简单类型(整数类型,如int,float,pointer或POD(计划旧数据)类型)的情况下添加它是有用的,这些值是零初始化的(或通过值初始化归零(这是有效的)同样的事情),取决于使用的是哪个版本的C ++。

    无论如何,底线我s,使用简单类型的映射将自动将新项初始化为零。因此,在某些情况下,无需担心明确指定默认初始值。

     
    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
    

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

        
    2
    2017-05-23 12:34:30Z

    一种解决方法是使用map::at()而不是[]。 如果密钥不存在,at将抛出异常。 更好,这也适用于矢量,因此适用于通用编程,您可以使用矢量交换地图。

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

        
    2
    2019-01-16 17:55:30Z

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

     
    template < class Key, class T, class Compare = less<Key>,
           class Allocator = allocator<pair<const Key,T> > > class map;
    
        
    1
    2010-02-25 12:06:45Z
    1. operator[]返回通过调用T()创建的对象,无论分配器做什么。
      2010-02-25 12:17:09Z
    2. @sbi:地图是否调用分配器construct方法?我想,有可能改变这种状况。我怀疑construct的功能除了new(p) T(t);以外还没有其他功能。编辑:事后看来这是愚蠢的,否则所有的价值观都是一样的:P我的咖啡在哪儿......
      2010-02-25 16:04:25Z
    3. @ GMan:我的C ++ 03副本(在23.3.1.2中)说operator[]返回(*((insert(make_pair(x, T()))).first)).second。所以,除非我遗漏了什么,否则这个答案是错误的。
      2010-02-26 12:44:28Z
    4. 你是对的。但这对我来说似乎不对。为什么他们不使用allocator函数插入?
      2010-02-26 16:12:38Z
    5. @sbi:不,我同意这个答案是错误的,但出于不同的原因。编译器确实用insert执行了T(),但是内部插入是它将使用分配器获取内存用于新的T然后使用给定的参数(即construct)在该内存上调用T()。因此确实可以改变operator[]的行为以使其返回其他内容,但是分配器无法区分它被调用的原因。因此,即使我们使construct忽略它的参数并使用我们的特殊值,这意味着每个构造的元素都有该值,这是坏的。
      2010-02-26 19:10:46Z
    6. 醇>
来源放置 这里