首页 > 解决方案 > 更改 for-loop 的第三部分后 unexpend for-loop 结果

问题描述

当我for-loop在我的源文件中使用时,我得到了一个未使用的结果。这是最小的源文件(我隐藏了文件头和函数print_set):

// the main function
int main(void) {
    set<int> test{3, 5};
    print_set(test);

    for (auto it = test.begin(); it != test.end();) {
        auto node = test.extract(it);
        ++it;
    }
    print_set(test);
}

然后我使用命令编译并运行:

$ g++ --version
g++ (Dedian 8.3.0-6) 8.3.0
... (not very important infomation for this question)
$ g++ -std=c++17 temp.cpp
$ ./a.out
[ 3 5 ]
[ ]

现在一切顺利,但是在我将for-loop部分更改为此之后(我替换++itfor-loop头部的第三部分):

    for (auto it = test.begin(); it != test.end(); ++it) {
        auto node = test.extract(it);
    }

现在结果是:

$ ./a.out
[ 3 5 ]
zsh: segmentation fault (core dumped) ./a.out

Zsh是我正在使用的 Linux shell,它不是很重要的信息。for-loop看了一些类似这样的网页后,我还是不知道为什么?它是一个错误吗?为什么这些不相等并且有两个不同的结果?因为方法extract?但是为什么第一部分可以运行呢?

谢谢。

标签: c++for-loopc++17extract

解决方案


std::set<T>::extract()文档中:

提取节点会使提取元素的迭代器无效。

调用 后test.extract(it),迭代器it不再有效。即使将其递增或将其与另一个迭代器进行比较也不是定义的操作。it提取后您可以做的唯一安全的事情是:

  • 让它被破坏。
  • 为其分配一个有效的迭代器。

其他任何事情都会导致未定义的行为,因此您问题中的两个代码示例都会调用未定义的行为。问为什么一个人做的事情与另一个人不同是一个毫无意义的问题。每次程序运行时,它们可能都会做不同的事情——包括你想让代码做什么!

无法推理未定义的行为,尝试这样做几乎总是浪费时间。

要修复代码,您需要复制迭代器,增加原始值,然后提取副本。这正是后增量运算符所做的:

for (auto it = test.begin(); it != test.end();) {
    auto node = test.extract(it++);
}

在此示例中,在提取发生之前it发生了突变。此示例具有明确定义的行为,并将逐个提取集合中的每个元素。


推荐阅读