【问题标题】:std::map erase - pass iterator to wrong mapstd::map 擦除 - 将迭代器传递给错误的地图
【发布时间】:2019-08-21 03:28:37
【问题描述】:

拿这个 C++ 的 sn-p:

#include <map>

int main() {
    std::map<int, int> m1;
    m1[1] = 2;

    std::map<int, int> m2;
    m2[3] = 4;
    m1.erase(m2.begin());

    return m2.size();
}

在神螺栓上:https://godbolt.org/z/mJBszn

这感觉一定是未定义的行为。那是对的吗?如果是这样,标准的哪一部分是这样说的?

【问题讨论】:

  • 它应该给出未定义的行为。因为,在 m1.erase(m2.begin()) 中,m2.begin() 指向的位置对 m1 无效。

标签: c++ language-lawyer undefined-behavior stdmap


【解决方案1】:

这感觉一定是未定义的行为。对吗?

是的。

如果是这样,标准的哪一部分是这样说的?

标准在 [associative.reqmts] 注释 8 中消除了这种愚蠢。我是 citing n4659,因为它是我与 C++17 的链接并接近 C++17 的内容。目前 C++20 仍然是一个移动的目标。

深入了解 [tab:container.assoc.req],我们发现了三个采用迭代器的 erase 重载,

a.erase(q)
a.erase(r)
a.erase(q1, q2)

其中a.erase(r) 是提问者感兴趣的。

该表仅说明程序运行时会发生什么;然而,该表的序言指出

q 表示a 的有效可解引用常量迭代器,r 表示a 的有效可解引用迭代器,[q1q2) 表示a 中的常量迭代器的有效范围

换句话说,如果迭代器 r 不是来自 map aaend 迭代器,或者已经失效或以其他方式呈现不可引用,则合同被破坏并且结果已经未定义。

我包括 qq1q2 以显示规则对于常量迭代器和迭代器范围是相同的。

【讨论】:

    【解决方案2】:

    这都是参考 C++17 的。

    编辑 2** 我收回我最初所说的话,我只是通过部分 c++ 标准和评论。在 §26.2.6 中,关联容器上下文中 a.erase(r) 的标准状态是“如果不存在这样的元素,则返回 a.end()”。但是,该标准还声明“r 表示对 a 的有效可解引用迭代器”

    由于 m2.begin() 不是这种情况,这不符合标准,因此是未定义的行为。

    【讨论】:

    • 你找到了正确的东西,但是you have to look up a bit 在表格前面的注释中它说 q 表示对 a 的有效可解引用常量迭代器,r 表示对 a 的有效可解引用迭代器 在这种情况下,我们需要表格中的a.erase(r)
    • 请注意,该链接指向标准的较新版本。 This should be closer to C++17
    • @user4581301 啊,很好。我一定是迷路了。我收回我的收回。它是未定义的行为!
    • 更新答案,您将获得我的支持。旁注:由于草稿之间的节号经常变化(更不用说在正式版本之间),在这种情况下更喜欢使用节标签 [associative.reqmts],所以五年后的人们有更好的机会在 C++ 24 或其他语言中找到正确的部分。
    猜你喜欢
    • 2021-05-08
    • 1970-01-01
    • 2013-04-22
    • 1970-01-01
    • 2017-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多