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

请教一个 STL 的问题

  •  
  •   pabupa · 2021-04-04 20:53:42 +08:00 · 2559 次点击
    这是一个创建于 1079 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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   ·   我们的愿景   ·   实用小工具   ·   3493 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 11:08 · PVG 19:08 · LAX 04:08 · JFK 07:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.