假设我们有两个映射,它们具有一些公共键但值不同,我想迭代两个映射的“交集”,其中键相同。然后我想执行一个变换函数
f(key, value_in_map1, value_in_map2)
。
我想使用
range views
来完成它,这样我就可以进一步进行管道化。我在 range-v3 中尝试了 ranges::views::set_intersection
,但它没有按预期工作:
#include <map>
#include <iostream>
#include <range/v3/all.hpp>
int main () {
std::map<int, int> m1 {{0, 0}, {1, 1}, {2, 2}};
std::map<int, int> m2 {{0, 0}, {2, 1}, {3, 2}};
auto res = ranges::views::set_intersection(m1, m2);
for(auto&& p : res) {
std::cout << p.first << ' ' << p.second << '\n';
// output: 0 0
}
/* Here is what I expect for the desired view
auto res = map_intersection(m1, m2);
for(auto&& [key, value_in_m1, value_in_m2] : res) {
std::cout << p.first << ' ' << p.second << '\n';
// expected output:
// 0 0 0
// 2 2 1
}
// my desired syntax
map_intersection(m1, m2)
| ranges::views::transform([](int key, int value_in_m1, int value_in_m2) {
// use the values here
})
| more_views
*/
return 0;
}
https://godbolt.org/z/3cbMY9n7b显示了上面的代码。
如果range-v3中不存在这样的视图(上面注释代码中的
map_intersection
),如何实现?
v3 或 STL 中没有可用的视图或算法。主要问题是所有
set_*
函数仅返回第一个范围的值。在您的示例中,您正在比较这对地图,因此您只能得到 0 0
结果。您可以通过使用自定义投影仪或自定义比较器来解决此问题,但是您会得到 0 0
和 2 2
(来自第一个范围),而 2 1
(来自第二个范围)缺失。
您需要的是类似
set_intersection_transform
的东西,它在返回结果之前转换两个迭代器。使用 v3 编写这样的视图很困难,您可以查看现有的 source 和 manual 关于如何编写自定义视图。实现这样的视图可能超出了 SO 的范围。
请注意,
set_intersection
在 C++ 20 中不是作为视图实现的,而是作为算法实现的。您可以轻松地根据您的需要调整 cppreference 中的示例。这是一个复制粘贴,删除了所有 C++ 20 内容(包括返回类型),并添加了我添加的转换功能。尽管它缺乏所有 v3 约束并且不是视图,但它可能是您工作的起点。
#include <range/v3/all.hpp>
struct set_intersection_transform_fn {
template <typename I1, typename S1, typename I2, typename S2, typename O,
typename Comp, typename Proj1, typename Proj2, typename Func>
constexpr void operator()(I1 first1, S1 last1, I2 first2, S2 last2, O result,
Comp comp, Proj1 proj1, Proj2 proj2,
Func func) const {
while (!(first1 == last1 or first2 == last2)) {
if (std::invoke(comp, std::invoke(proj1, *first1),
std::invoke(proj2, *first2))) {
++first1;
} else if (std::invoke(comp, std::invoke(proj2, *first2),
std::invoke(proj1, *first1))) {
++first2;
} else {
*result = std::invoke(func, *first1, *first2), ++first1, ++first2,
++result;
}
}
}
template <typename R1, typename R2, typename O, typename Comp, typename Proj1,
typename Proj2, typename Func>
void operator()(R1&& r1, R2&& r2, O result, Comp comp, Proj1 proj1,
Proj2 proj2, Func func) const {
(*this)(ranges::begin(r1), ranges::end(r1), ranges::begin(r2),
ranges::end(r2), std::move(result), std::move(comp),
std::move(proj1), std::move(proj2), std::move(func));
}
};
inline constexpr set_intersection_transform_fn set_intersection_transform{};
输出为
0 0 0
和 2 2 1
:
std::map<int, int> m1{{0, 0}, {1, 1}, {2, 2}};
std::map<int, int> m2{{0, 0}, {2, 1}, {3, 2}};
std::vector<std::tuple<int, int, int>> result;
const auto proj = &std::map<int, int>::value_type::first;
const auto transform = [](const auto& a, const auto& b) {
return std::tuple{a.first, a.second, b.second};
};
set_intersection_transform(m1, m2, std::back_inserter(result), std::less<>{},
proj, proj, transform);
for (const auto& [key, value_in_m1, value_in_m2] : result) {
std::cout << key << " " << value_in_m1 << " " << value_in_m2 << std::endl;
}