当键不存在时,是否可以指定默认值std::map
的operator[]
返回值?
Answers:
不,没有。最简单的解决方案是编写自己的免费模板函数来执行此操作。就像是:
#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;
}
operator[]
与默认值,默认值应该被插入到里面的地图if ( it == m.end() )
块
C ++标准(23.3.1.2)指定新插入的值是默认构造的,因此map
它本身不提供执行此操作的方法。您的选择是:
operator[]
插入该默认值。更通用的版本,支持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");
C ++ 17提供了try_emplace
确切的功能。它为值构造函数获取键和参数列表,并返回一个对:aniterator
和bool
。:http : //en.cppreference.com/w/cpp/container/map/try_emplace
无法指定默认值-它始终是由默认值(零参数构造函数)构造的值。
实际上,operator[]
可能做得比您预期的要多,好像映射中给定键的值不存在时,它将使用默认构造函数中的值插入一个新值。
find
,如果给定键不存在任何元素,则可以使用它返回结束迭代器。
find
在这种情况下的时间复杂度是多少?
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
请参阅类型名称后面的括号是否与new有所区别?有关此问题的更多详细信息。
也许您可以给自定义分配器分配要使用的默认值。
template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;
operator[]
T()
无论分配器做什么,都将返回通过调用创建的对象。
construct
方法吗?我认为,有可能改变这种状况。我怀疑一个construct
函数除了new(p) T(t);
格式不正确外没有做其他事情。编辑:事后看来这是愚蠢的,否则所有值都将是相同的:P我的咖啡在哪里...
operator[]
返回(*((insert(make_pair(x, T()))).first)).second
。因此,除非我缺少任何东西,否则这个答案是错误的。
insert
使用了T()
,但是在insert里面是它将使用分配器获取新内存的时间,T
然后construct
使用给定的参数调用该内存T()
。因此,确实有可能更改行为operator[]
以使其返回其他内容,但是分配器无法区分被调用的原因。因此,即使我们construct
忽略它的参数并使用我们的特殊值,这也意味着每个构造的元素都具有该值,这很糟糕。
扩展答案https://stackoverflow.com/a/2333816/272642时,此模板函数使用std::map
的key_type
和mapped_type
typedef来推断key
and的类型def
。没有这些typedef的容器将无法使用。
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;
}
这使您可以使用
std::map<std::string, int*> m;
int* v = getWithDefault(m, "a", NULL);
无需像这样进行争论std::string("a"), (int*) NULL
。
使用std::map::insert()
。
意识到我参加这个聚会已经很晚了,但是如果您对operator[]
自定义默认设置的行为感兴趣(即,找到具有给定键的元素,如果不存在,则在地图中插入带有选择默认值并返回对新插入值或现有值的引用),C ++ 17之前的版本已经可以使用std::map::insert()
。insert
如果键已经存在,则不会实际插入,而是将迭代器返回到现有值。
假设您想要一个字符串到整数的映射,如果还不存在该键,则插入默认值42:
std::map<std::string, int> answers;
int count_answers( const std::string &question)
{
auto &value = answers.insert( {question, 42}).first->second;
return value++;
}
int main() {
std::cout << count_answers( "Life, the universe and everything") << '\n';
std::cout << count_answers( "Life, the universe and everything") << '\n';
std::cout << count_answers( "Life, the universe and everything") << '\n';
return 0;
}
它应该输出42、43和44。
如果构造映射值的成本很高(如果复制/移动键或值类型很昂贵),那么这将导致明显的性能损失,我认为C ++ 17可以避免这种情况try_emplace
。