c++ 如何使用for循环用反向迭代器调用erase

iswrvxsc  于 2023-05-08  发布在  其他
关注(0)|答案(2)|浏览(164)

关于这里提供的答案:如何使用反向迭代器调用erase
下面的代码在g++ 4.8.4中编译时会导致分段错误(在++it上),其中-std=c++11。我误解了答案吗?

std::map<int,int> testmap;
  testmap[0] = 1;
  for(auto it=testmap.rbegin(); it!=testmap.rend(); ++it) {
    testmap.erase( std::next(it).base() );
  }
syqv5f0l

syqv5f0l1#

erase使迭代器无效,所以你必须从erase的返回值重新构造它:

it = std::map<int,int>::reverse_iterator(testmap.erase( std::next(it).base() ));

C++11:

it = decltype(it){testmap.erase( std::next(it).base() )};

C++17:

it = std::reverse_iterator(testmap.erase( std::next(it).base() ));

Demo
为了完整起见,下面是从原始问题中更正的循环的样子(注意迭代器增量已从for(...)中删除):

for (auto rit = testmap.rbegin(); rit != testmap.rend(); /* empty */) {
    if (WE_WANT_TO_ERASE(rit)) {
        rit = decltype(rit){ testmap.erase(std::next(rit).base()) };
    } else {
        ++rit;
    }
}
csga3l58

csga3l582#

在使用了这个习惯用法之后,我认为对Jarod42的答案中的循环进行修改是为了使事情更安全并保持典型的for(;;)循环细节:

for (auto it = testcont.rbegin(), nit = it; it != testcont.rend(); it = nit) {
    nit = next(it);

    // whatever... maybe a continue somewhere or maybe not

    if (WE_WANT_TO_ERASE(it)) {
        nit = decltype(it){ testcont.erase(std::next(it).base()) };
    }

    // whatever... maybe a continue somewhere or maybe not
}

在其他答案中使用循环太危险了。如果在循环中的某个地方轻率地添加一个continue;,而没有先递增迭代器,结果将是一个无限循环。因为,乍一看,这看起来像一个普通的for(;;)循环,我相信这迟早会发生。类似地,如果循环中有分支,并且其中一个分支忽略了递增迭代器,则会引入另一个错误。最后,如果你做了一个erase(),你需要在递增迭代器之前确保continue,否则你会有另一个bug。
使用上面修改过的循环,该循环可以像普通的for(;;)循环一样处理。技巧是在循环体的第一行增加nit(“下一个迭代器”)。那你就不用担心了您需要更新nit的唯一时间是如果您正在执行erase()。其他一切都像人们期望的for循环一样工作。
最后一点:我最初问的是关于Map的问题,但这也适用于vectorlist等。

相关问题