C++ 迭代时从集合中删除的最有效方法

C++ 迭代时从集合中删除的最有效方法,c++,C++,迭代时从集合中删除的最有效方法是什么?我想到了两种方法,哪一种最好?还有更好的办法吗 void WaitForFiles(std::set<string> files) { while (files.size() > 0) { std::set<string> found_files; for (const auto& file : files) { if (Exists(file)) { found_file

迭代时从集合中删除的最有效方法是什么?我想到了两种方法,哪一种最好?还有更好的办法吗

void WaitForFiles(std::set<string> files) {
  while (files.size() > 0) {
    std::set<string> found_files;
    for (const auto& file : files) {
      if (Exists(file)) {
        found_files.insert(file);
      }
    }
    for (const auto& found_file : found_files) {
      files.erase(file);
    }
  }
}
void WaitForFiles(std::set files){
而(files.size()>0){
std::设置已找到的文件;
用于(常量自动和文件:文件){
如果(存在(文件)){
找到_文件。插入(文件);
}
}
用于(常量自动和查找文件:查找文件){
文件。擦除(文件);
}
}
}
使用set_差异:

void WaitForFiles(std::set<string> files) {
  while (files.size() > 0) {
    std::set<string> found_files;
    for (const auto& file : files) {
      if (Exists(file)) {
        found_files.insert(file);
      }
    }
    std::set<string> difference;
    std::set_difference(files.begin(), files.end(),
                        found_files.begin(), found_files.end(),
                        std::inserter(difference, difference.end()));
    files = difference;
  }
}
void WaitForFiles(std::set files){
而(files.size()>0){
std::设置已找到的文件;
用于(常量自动和文件:文件){
如果(存在(文件)){
找到_文件。插入(文件);
}
}
std::集差;
std::set_差异(files.begin()、files.end(),
找到\u文件。开始(),找到\u文件。结束(),
std::inserter(difference,difference.end());
文件=差异;
}
}
请注意,以下崩溃:

void WaitForFiles(std::set<string> files) {
  while (files.size() > 0) {
    for (const auto& file : files) {  // <-- seg fault after first erase
      if (Exists(file)) {
        files.erase(file);
      }
    }
  }
}
void WaitForFiles(std::set files){
而(files.size()>0){

for(const auto&file:files){/在基于范围的for循环中从集合中删除是未定义的行为(即使它似乎有效)。基于范围的for循环在内部使用迭代器,删除元素会使迭代器无效

但是
std::set::erase
会将有效迭代器返回到
std::set
中的下一个元素,因此可以使用显式迭代器循环:

for(auto itr = files.cbegin(); itr != files.cend();) {
  if (exists(*itr)) {
    std::cout << "Found file: " << *itr << "\n";
    itr = files.erase(itr);
  } else
    ++itr;
}
for(auto-itr=files.cbegin();itr!=files.cend();){
如果(存在(*itr)){

std::cout前两个示例看起来不太理想,因为它们都涉及对集合进行两次迭代。第三个示例不稳定,因为在使用
std::set::erase
更改集合使迭代器无效后,您继续使用迭代器

我认为以下内容应该是合理有效的。但是,正如其他人提到的,可能值得用
std::vector
替换
std::set found\u files

#include <set>
#include <string>
#include <iostream>

/**
 * Return true for every other call
 */
bool Exists(std::string const&)
{
    static int i = 0;
    return ++i % 2;
}

std::set<std::string> find_existing_files(std::set<std::string>& files)
{
    std::set<std::string> found_files;

    for(auto file = files.begin(); file != files.end();)
    {
        if(!Exists(*file))
            ++file; // not erasing, keep going
        else
        {
            found_files.insert(*file);
            // replacing iterator with result of erase()
            // keeps the iterator valid
            file = files.erase(file);
        }
    }
    return found_files;
}

int main()
{
    std::set<std::string> files {"a", "b", "c", "d", "e"};

    for(auto const& file: find_existing_files(files))
        std::cout << "exists : " << file << '\n';

    for(auto const& file: files)
        std::cout << "missing: " << file << '\n';
}
从库基本知识TS的v2开始:

std::experimental::erase_if(files, Exists);
如果
存在
重载或是函数模板,请使用lambda:

std::experimental::erase_if(files, [](const auto& f) { return Exists(f); });

这已经在VS2015中实现了。

与其将
找到的_文件
设置为
集合
,不如将其设置为
向量
。与其无限期地使用100%线程时间,不如使用一些操作系统功能,例如文件夹中的更改通知等。“基于事件”的好处还有一点是,你总是会在文件出现时立即删除它,并且“在迭代时删除”问题根本不存在。MarkRansom,这将如何改善情况?BitTickler,不幸的是,我使用的文件系统不支持通知。假设该文件是第三方网站。好吧,但如果你循环,为什么要要求效率?如果你的perma循环每500us或每1ms循环一次,这又有什么关系?从该集合使基于范围的for循环正在使用的隐藏迭代器无效。相反,您需要使用显式迭代器编写循环,并使用
erase
方法返回的新迭代器。现在您总是返回一个空集:)@T.C.呵呵,这是同时编辑许多内容的问题。我非常喜欢这样答案是因为该链接解释了什么erase_if相当于.Side question.is files.erase(itr)比files.erase(*itr)快,或者它是实现定义的吗?@fulfentowl第一个几乎肯定要快。第二个必须找到集合中的元素,第一个没有。@fulfentowl files.erase(*itr)不是此选项;它不返回迭代器(而且不能,因为它可能正在擦除多个元素)。
std::experimental::erase_if(files, [](const auto& f) { return Exists(f); });