在基于范围的循环中删除地图元素

Ste*_*ein 3 c++ unordered-map std-ranges range-based-loop

我想根据某些条件从地图中删除一些元素:

#include <unordered_map>
#include <ranges>
#include <iostream>

int main() {

    std::unordered_map<int, int> numbers = {{1,2}, {2,1}, {3,2}, {4,5}};

    auto even = [](auto entry){return entry.second %2 == 0;};
    for(auto& [key, val] : numbers | std::views::filter(even)) {
        numbers.erase(val);
    }

    for(auto& [key, val] : numbers) {
        std::cout << key << " " << val << "\n";
    }
}
Run Code Online (Sandbox Code Playgroud)

但似乎我正在使基于范围的循环所需的迭代器无效:

4 5
3 2
1 2
Run Code Online (Sandbox Code Playgroud)

我知道如何使用迭代器显式地执行此操作,但是是否有一种基于范围的简洁方法来删除基于过滤器的元素?

sor*_*abz 5

我建议你使用std::erase_if(),如下所示:

std::erase_if(numbers, [](auto entry) {return entry.second % 2 == 0; });
Run Code Online (Sandbox Code Playgroud)

如果您想要一个范围解决方案,并且不需要就地更改,您可以执行如下操作(此代码仅在 C++23 中编译):

numbers = numbers | std::views::filter(even) | std::ranges::to<decltype(numbers)>();
Run Code Online (Sandbox Code Playgroud)

我不确定,但根据文档,下面的代码可能比上面的正常范围代码具有更好的性能:

auto temp_numbers = numbers | std::views::filter(even) | std::ranges::to<decltype(numbers)>();
numbers.swap(temp_numbers);
Run Code Online (Sandbox Code Playgroud)

operator=正如你在for中看到的std::unordered_map,它的移动赋值运算符的复杂度是线性的,但是它的移动构造函数的复杂度是恒定的swap(),并且它的方法的复杂度也是恒定的,所以看起来性能更好。但是,我对此没有任何基准。