std::map 默认值
有没有办法指定键不存在时 std::map
的 operator[]
返回的默认值?
Is there a way to specify the default value std::map
's operator[]
returns when an key does not exist?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
无法指定默认值 - 它始终是由默认值(零参数构造函数)构造的值。
事实上,operator[] 的作用可能比您预期的要多,就好像映射中给定键不存在值一样,它将使用默认构造函数中的值插入一个新值。
There is no way to specify the default value - it is always value constructed by the default (zero parameter constructor).
In fact
operator[]
probably does more than you expect as if a value does not exist for the given key in the map it will insert a new one with the value from the default constructor.更通用的版本,支持C++98/03和更多容器
适用于通用关联容器,唯一的模板参数是容器类型本身。
支持的容器:
std::map
、std::multimap
、std::unordered_map
、std::unordered_multimap
、wxHashMap、QMap、QMultiMap、QHash、QMultiHash 等。用法:
这里有一个类似的使用包装类的实现,它更类似于Python中
dict
类型的方法get()
:https://github.com/hltj/wxMEdit/blob/master/src/xm/xm_utils.hpp< /a>用法:
More General Version, Support C++98/03 and More Containers
Works with generic associative containers, the only template parameter is the container type itself.
Supported containers:
std::map
,std::multimap
,std::unordered_map
,std::unordered_multimap
,wxHashMap
,QMap
,QMultiMap
,QHash
,QMultiHash
, etc.Usage:
Here is a similar implementation by using a wrapper class, which is more similar to the method
get()
ofdict
type in Python: https://github.com/hltj/wxMEdit/blob/master/src/xm/xm_utils.hppUsage:
C++17 之前的版本,使用
std::map::insert()
,较新版本使用try_emplace()
。这可能违反直觉,但这些函数实际上具有带有自定义默认值的operator[]
行为。意识到我参加这个聚会已经很晚了,但是如果您对具有自定义默认值的
operator[]
的行为感兴趣(即:查找具有给定键的元素,如果它不是)如果当前插入一个选定的默认值并返回对新插入的值或现有值的引用),在 C++17 之前已经有一个函数可供您使用:std::地图::插入()。如果键已经存在,则
insert
不会实际插入,而是返回一个指向现有值的迭代器。假设您想要一个字符串到整数的映射,并在键不存在时插入默认值 42:
这应该输出 42、43 和 44。
如果构建映射值的成本很高(如果复制/移动键或值类型的成本很高),这会带来显着的性能损失,这可以通过 C++17 的
try_emplace()
来规避。Pre-C++17, use
std::map::insert()
, for newer versions usetry_emplace()
. It may be counter-intuitive, but these functions effectively have the behaviour ofoperator[]
with custom default values.Realizing that I'm quite late to this party, but if you're interested in the behaviour of
operator[]
with custom defaults (that is: find the element with the given key, if it isn't present insert a chosen default value and return a reference to either the newly inserted value or the existing value), there is already a function available to you pre C++17:std::map::insert()
.insert
will not actually insert if the key already exists, but instead return an iterator to the existing value.Say, you wanted a map of string-to-int and insert a default value of 42 if the key wasn't present yet:
which should output 42, 43 and 44.
If the cost of constructing the map value is high (if either copying/moving the key or the value type is expensive), this comes at a significant performance penalty, which would be circumvented with C++17's
try_emplace()
.一种解决方法是使用
map::at()
而不是[]
。如果键不存在,
at
会抛出异常。更好的是,这也适用于向量,因此适合通用编程,您可以将映射与向量交换。
对未注册的密钥使用自定义值可能很危险,因为该自定义值(如 -1)可能会在代码中进一步处理。除了例外情况,更容易发现错误。
One workaround is to use
map::at()
instead of[]
.If a key does not exist,
at
throws an exception.Even nicer, this also works for vectors, and is thus suited for generic programming where you may swap the map with a vector.
Using a custom value for unregistered key may be dangerous since that custom value (like -1) may be processed further down in the code. With exceptions, it's easier to spot bugs.
扩展答案 https://stackoverflow.com/a/2333816/272642,此模板函数使用
std ::map
的key_type
和mapped_type
typedef 来推断key
和def
的类型。这不适用于没有这些 typedef 的容器。
即可使用,例如
std::string("a"), (int*) NULL
。这允许您无需强制转换参数
Expanding on the answer https://stackoverflow.com/a/2333816/272642, this template function uses
std::map
'skey_type
andmapped_type
typedefs to deduce the type ofkey
anddef
.This doesn't work with containers without these typedefs.
This allows you to use
without needing to cast the arguments like
std::string("a"), (int*) NULL
.如果您可以访问 C++17,我的解决方案如下:
这允许您在每次使用映射时指定“默认值”。这可能不一定是您想要或需要的,但为了完整起见,我将其发布在这里。该解决方案非常适合函数式范例,因为地图(和字典)通常以这种方式使用:
If you have access to C++17, my solution is as follows:
This allows you to specify a 'default value' at each use of the map. This may not necessarily be what you want or need, but I'll post it here for the sake of completeness. This solution lends itself well to a functional paradigm, as maps (and dictionaries) are often used with such a style anyway:
使用C++20,编写这样的getter很简单:
如@425nesp和КоеКто 注意到,仅当
getOrDefault
作为常量表达式调用时,编译器才不会编译具有未定义行为的代码。如果将
getOrDefault
作为常规函数调用,则最终可能会得到对已销毁临时文件的引用。例子
With C++20 it is simple to write such getter:
As @425nesp and КоеКто noticed, the compiler won't compile code with undefined behavior only if
getOrDefault
called as constant expression.If
getOrDefault
called as regular function, it possible to end up with a reference to the destroyed temporary.Example
也许您可以提供一个自定义分配器,它可以分配您想要的默认值。
Maybe you can give a custom allocator who allocate with a default value you want.
这是一种正确的方法,如果调用者传入映射类型的左值引用,它将有条件地返回引用。
Here is a correct approach that will conditionally return a reference if the caller passes in an lvalue reference to the mapped type.
如果您想继续使用
operator[]
,就像您不必指定除T()
中的默认值(其中>T
是值类型),您可以继承T
并在构造函数中指定不同的默认值:但是,如果
T
是原始类型,请尝试this:另请参阅围绕基本类型的 C++ 类包装器
If you would like to keep using
operator[]
just like when you don't have to specify a default value other than what comes out fromT()
(whereT
is the value type), you can inheritT
and specify a different default value in the constructor:However, if
T
is a primitive type, try this:See also C++ Class wrapper around fundamental types
虽然这并不能完全回答问题,但我已经用这样的代码规避了这个问题:
While this does not exactly answer the question, I have circumvented the problem with code like this:
不,没有。最简单的解决方案是编写您自己的免费模板函数来执行此操作。类似于:
C++11 更新
目的:考虑通用关联容器,以及可选的比较器和分配器参数。
No, there isn't. The simplest solution is to write your own free template function to do this. Something like:
C++11 Update
Purpose: Account for generic associative containers, as well as optional comparator and allocator parameters.
C++17 提供的
try_emplace
正是这样做的。它需要一个键和一个值构造函数的参数列表,并返回一对:一个迭代器和一个布尔值。:http://en.cppreference.com/w/cpp/container/map/try_emplaceC++17 provides
try_emplace
which does exactly this. It takes a key and an argument list for the value constructor and returns a pair: aniterator
and abool
.: http://en.cppreference.com/w/cpp/container/map/try_emplaceC++ 标准 (23.3.1.2) 指定新插入的值是默认构造的,因此
map
本身不提供执行此操作的方法。您的选择是:operator[]
来插入该默认值。The C++ standard (23.3.1.2) specifies that the newly inserted value is default constructed, so
map
itself doesn't provide a way of doing it. Your choices are:operator[]
to insert that default.正如其他答案所说,该值是使用默认构造函数初始化的。然而,补充一点是有用的,在简单类型(整数类型,如 int、float、指针或 POD(计划旧数据)类型)的情况下,值被零初始化(或通过值初始化归零(这实际上是相同的事情),具体取决于使用的 C++ 版本)。
无论如何,底线是,具有简单类型的映射将自动对新项目进行零初始化。所以在某些情况下,无需担心显式指定默认初始值。
请参阅 添加括号在类型名称之后 make a Difference 与 new? 有关此事的更多详细信息。
The value is initialized using the default constructor, as the other answers say. However, it is useful to add that in case of simple types (integral types such as int, float, pointer or POD (plan old data) types), the values are zero-initialized (or zeroed by value-initialization (which is effectively the same thing), depending on which version of C++ is used).
Anyway, the bottomline is, that maps with simple types will zero-initialize the new items automatically. So in some cases, there is no need to worry about explicitly specifying the default initial value.
See Do the parentheses after the type name make a difference with new? for more details on the matter.