请教一个 STL 的问题

2021-04-04 20:53:42 +08:00
 pabupa
map<int, int> m;
m[12] = 45; // 这是怎么实现的?

它只是返回了一个int&,而且还不存在。为什么能赋值啊?

源码真的看不懂,……

2605 次点击
所在节点    C++
21 条回复
pabupa
2021-04-04 20:56:57 +08:00
(#`O′)喂?没人吗?
keygen88
2021-04-04 20:57:33 +08:00
重载了[]吧,
jmc891205
2021-04-04 21:00:51 +08:00
nightwitch
2021-04-04 21:07:30 +08:00

多看标准啊。标准明确告诉了如果 key 不存在,会先执行一次 insert 。

llvm 真正的实现在这里
https://github.com/llvm-mirror/libcxx/blob/78d6a7767ed57b50122a161b91f59f19c9bd0d19/include/__tree#L2126

它会先寻找内部是否存在这个值,如果不存在就会构造。
pabupa
2021-04-04 21:10:02 +08:00
@nightwitch #4 那对于只读的情况来说,不就多余了吗?而且如果 V 不能无参构造的话,不久不能用了吗?
hello2060
2021-04-04 21:10:11 +08:00
已经忘了 c++了,这个你不是记住就行了吗?

它只是返回了一个 int&,而且还不存在 ------ 不存在你是怎么知道的,也许这个表达式在左边的时候会自动创建呢?你预设它不存在,所以无法赋值,但明明可以赋值,那不就说明你不存在的假设是错误的吗
hello2060
2021-04-04 21:10:51 +08:00
@pabupa 只读不会插入吧。。
pabupa
2021-04-04 21:12:44 +08:00
@hello2060 #7 它没有办法区分的……
ipwx
2021-04-04 21:13:49 +08:00
只读调用 const value_type& operator[](const key_type&) const
可变调用 value_type7 operator[](const key_type&)

函数末尾有没有修饰符 const,在 C++ 里面是两个不同的成员函数。
====

但是这里有个坑。如果你操作的是可变对象,你的 value_type 有默认构造函数,如果你使用

std::cout << m[key] << std::endl;

那么如果 key 不存在,在某些版本的 STL 真的会创建出这个对象。我就被坑过。
across
2021-04-04 21:15:06 +08:00
mapped_type& operator[] (key_type&& k);

C++11 里面加了个右值引用
across
2021-04-04 21:17:25 +08:00
@across 哦,答歪了。 这个取值不是 const 的,返回时内部已经创建过变量了。找标准解释都有说的。
http://www.cplusplus.com/reference/map/map/operator[]/
nightwitch
2021-04-04 21:17:37 +08:00
@pabupa https://en.cppreference.com/w/cpp/container/map/operator_at
仔细看标准吧。
1. 只读的时候 map 提供了 at 函数,对于不存在的 key 会抛异常
2. operaotr[]强制要求支持无参构造, 否则编译的时候会过不了编译。
pabupa
2021-04-04 21:19:00 +08:00
@ipwx #9 谢谢,懂了……
@nightwitch #12 我和你有仇吗?……不过还是谢谢
mogg
2021-04-04 21:21:26 +08:00
只读用 find 判断迭代器,或者先 count 再取值
读取一次事实上要返回两个值,是否存在和值的内容。stl 的年代还没有 std::optional ( c++17 ),没有办法表示返回多个值,总不能 map[x] = y 给你报个 error 出来吧
nightl2018
2021-04-04 23:54:41 +08:00
只读用 find 先找一下存不存在。
```
auto it = m.find(x);
if (it != m.end() ) cerr<<*it<<endl;
else cerr<<"Not exist."<<endl;
```
nightl2018
2021-04-04 23:56:43 +08:00
初看觉得很怪,但是结合其他 STL 标准,就相当优美。比如 set:
```
set<int> s;
if(s.insert(x).second == true) cerr<<"Insert success"<<endl;
else cerr<<"Already exsit"<<endl;
```
Akiyu
2021-04-05 08:56:12 +08:00
好像很多人都已经回答了, 不过我还是说一下吧.

"它只是返回了一个 int&,而且还不存在。为什么能赋值啊?"

"它只是返回了一个 int&"
是的, 它只是返回了一个 int&. 但这已经够了. int 是你 map value 的类型, 而 & 保证你的修改生效于 map 内部数据.

"而且还不存在"
存在的, 对于 map 的 [] 重载而言, 当你访问的元素不存在时, 会创建一个(类型的零值). 而存在时, 会返回 valueType&. 所以无论如何, 都会有元素存在.
(PS: 这里有个点需要注意, 当你想知道 map 中是否存在一个元素时, 你不能用 []. 因为这一定会存在, 你或许可以用这个类型的零值来判断是否是新创建的, 但这并不好, 更好的方式是: mapName.find(key) == maoName.end(); )
FrankHB
2021-04-05 11:21:55 +08:00
@ipwx 麻烦先记清楚 map::operator[] 要求 non-const 再来科普,谢谢。

怎么这么久都没个提出来 map::operator[] 早就改用 try_emplace 定义的?

WG21 N4860
22.4.4.3 Element access
[map.access]
mapped_type& operator[](const key_type& x);
1 Effects:
Equivalent to:
return try_emplace(x).first->second;
mapped_type& operator[](key_type&& x);
2 Effects:
Equivalent to:
return try_emplace(move(x)).first->second;

真折腾源码也行,给你个现成没到处 __ 的实现好了,C++11 下实现 C++17+ API,附加保证 incomplete value_type 能用:

https://github.com/FrankHB/YSLib/blob/master/YBase/include/ystdex/map.hpp#L313
https://github.com/FrankHB/YSLib/blob/master/YBase/include/ystdex/tree.h#L1885

不过可能不如理解个外挂式 try_emplace 的逻辑顶用点:

https://github.com/FrankHB/YSLib/blob/master/YBase/include/ystdex/container.hpp#L1039
opentrade
2021-04-05 13:08:09 +08:00
想到读研的时候有一次一个师弟问我为什么 Cpp 里函数里的局部变量外边不能访问。
unlighted
2021-04-06 11:43:49 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/767994

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX