请教一个 STL 的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
pabupa
V2EX    C++

请教一个 STL 的问题

  •  
  •   pabupa 2021-04-04 20:53:42 +08:00 3229 次点击
    这是一个创建于 1656 天前的主题,其中的信息可能已经有所发展或是发生改变。
    map<int, int> m; m[12] = 45; // 这是怎么实现的? 

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

    源码真的看不懂,……

    21 条回复    2021-06-04 01:39:48 +08:00
    pabupa
        1
    pabupa  
    OP
       2021-04-04 20:56:57 +08:00
    (#`O′)喂?没人吗?
    keygen88
        2
    keygen88  
       2021-04-04 20:57:33 +08:00
    重载了[]吧,
    jmc891205
        3
    jmc891205  
       2021-04-04 21:00:51 +08:00 via iPhone
    nightwitch
        4
    nightwitch  
       2021-04-04 21:07:30 +08:00

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

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

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

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

    C++11 里面加了个右值引用
    across
        11
    across  
       2021-04-04 21:17:25 +08:00
    @across 哦,答歪了。 这个取值不是 const 的,返回时内部已经创建过变量了。找标准解释都有说的。
    http://www.cplusplus.com/reference/map/map/operator[]/
    nightwitch
        12
    nightwitch  
       2021-04-04 21:17:37 +08:00   1
    @pabupa https://en.cppreference.com/w/cpp/container/map/operator_at
    仔细看标准吧。
    1. 只读的时候 map 提供了 at 函数,对于不存在的 key 会抛异常
    2. operaotr[]强制要求支持无参构造, 否则编译的时候会过不了编译。
    pabupa
        13
    pabupa  
    OP
       2021-04-04 21:19:00 +08:00
    @ipwx #9 谢谢,懂了……
    @nightwitch #12 我和你有仇吗?……不过还是谢谢
    mogg
        14
    mogg  
       2021-04-04 21:21:26 +08:00
    只读用 find 判断迭代器,或者先 count 再取值
    读取一次事实上要返回两个值,是否存在和值的内容。stl 的年代还没有 std::optional ( c++17 ),没有办法表示返回多个值,总不能 map[x] = y 给你报个 error 出来吧
    nightl2018
        15
    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
        16
    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
        17
    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
        18
    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
        19
    opentrade  
       2021-04-05 13:08:09 +08:00 via Android
    想到读研的时候有一次一个师弟问我为什么 Cpp 里函数里的局部变量外边不能访问。
    unlighted
        20
    unlighted  
       2021-04-06 11:43:49 +08:00 via Android
    paxol
        21
    paxol  
       2021-06-04 01:39:48 +08:00
    @pabupa std::string 以前用 COW 的时候也存在这么个问题,使用[]的时候无法判断作为左值还是右值,因此会导致不必要的 copy
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5565 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 03:35 PVG 11:35 LAX 20:35 JFK 23:35
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86