如何用C++20/23 std::views构建嵌套unordered_map结构?
用C20/C23 Views优化单次遍历的分组逻辑
我需要对输入的example_t结构体执行computeResults计算,将结果存入unordered_map<string, unordered_map<string, results_t>>——以结构体的attribute0为外层键、attribute1为内层键关联计算结果。目前我用range实现的方案需要多次遍历输入容器stuff,但原for循环仅需遍历一次。想知道在C20/C23中,有没有更优的views/ranges实现方式?
相关代码定义
struct results_t { int someNum; string someString; }; struct example_t { string attribute0; string attribute1; }; results_t computeResults(const example_t& input) { return { 0, "A" }; }
原for循环实现(单次遍历)
vector<example_t> stuff = { {"Food", "Bread"}, {"Food", "Apple"}, {"Animal", "Cat"}, {"Animal", "Dog"} }; unordered_map<string, unordered_map<string, results_t>> container = {}; for (const auto& thing : stuff) { const auto result = computeResults(thing); if (!container.contains(thing.attribute0)) container.insert({ thing.attribute0, {} }); container.at(thing.attribute0).insert({ thing.attribute1, result }); }
当前range实现(多次遍历)
auto uniqueAttribute0 = stuff | views::transform([](const auto & thing) { return thing.attribute0; }) | ranges::to<unordered_set<std::string>>(); auto v = uniqueAttribute0 | views::transform([&](const auto& key0) { auto innermap = stuff | views::filter([&](const auto& thing){ return thing.attribute0 == key0;}) | views::transform([](const auto& thing) { return make_pair(thing.attribute1, computeResults(thing)); }) | ranges::to<unordered_map<string, results_t>>(); return make_pair(key0, innermap); }) | ranges::to<unordered_map<string, unordered_map<string, results_t>>>();
优化方案:单次遍历的range实现
原for循环的单次遍历逻辑本身已经是效率最优的——只遍历一次输入,边计算边构建嵌套map。如果想用range/ranges库实现等价的单次遍历逻辑,可以借助折叠(fold)操作:
C++23 实现(用std::ranges::fold_left)
#include <ranges> #include <functional> auto container = std::ranges::fold_left( stuff, std::unordered_map<std::string, std::unordered_map<std::string, results_t>>{}, [](auto&& map, const auto& thing) { auto result = computeResults(thing); // 利用operator[]自动创建不存在的内层map,简化代码 map[thing.attribute0][thing.attribute1] = result; return std::forward<decltype(map)>(map); } );
C++20 实现(用std::accumulate配合range迭代器)
C++20标准库没有ranges::fold_left,但可以用std::accumulate结合range的首尾迭代器实现:
#include <numeric> auto container = std::accumulate( std::ranges::begin(stuff), std::ranges::end(stuff), std::unordered_map<std::string, std::unordered_map<std::string, results_t>>{}, [](auto&& map, const auto& thing) { auto result = computeResults(thing); map[thing.attribute0][thing.attribute1] = result; return std::forward<decltype(map)>(map); } );
说明
- 上述两种range方案都只遍历一次
stuff,和原for循环的时间复杂度、效率完全一致。 - 用
map[key0][key1]的写法比原循环更简洁,因为unordered_map的operator[]会自动插入默认构造的内层map(如果外层键不存在),省去了手动判断和插入的步骤。 - 惰性求值的views更适合做数据转换、过滤等无状态操作,而构建嵌套map属于有状态的累积操作,fold/accumulate是更贴合场景的选择,没必要强行用views实现。
内容的提问来源于stack exchange,提问作者ZZZ
相关产品推荐
相关产品推荐

