You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

std::views或range-v3是否有集合操作视图?若无该如何实现?

集合操作视图的存在性与实现方案

一、标准库与range-v3的支持情况

  • C标准库(截至C23):没有直接提供这类集合操作视图。标准库中的std::set_union、std::set_difference、std::set_intersection都是输出型算法,必须写入目标迭代器,无法直接生成惰性视图。
  • range-v3库:完全支持你期望的功能,提供了ranges::views::set_intersection、ranges::views::set_union、ranges::views::set_difference三个视图适配器,既支持直接调用views::set_xyz(a, b),也支持管道式语法a | views::set_xyz(b)。

二、自行实现思路(基于C++20 Ranges)

如果无法依赖range-v3,可基于C++20的视图规范,实现惰性求值的集合操作视图,核心是模拟标准集合算法的逻辑,避免提前生成完整结果。

1. 核心逻辑前提

所有集合操作视图要求输入范围已按升序排序,否则结果未定义。视图会惰性遍历两个输入范围,根据元素大小关系决定是否输出当前元素,并移动对应迭代器。

2. 示例实现:set_intersection视图

以下是完整的可运行实现,同时支持直接调用和管道语法:

#include <ranges>
#include <iterator>
#include <concepts>

namespace views {
    // 核心逻辑实现:处理两个已排序范围的交集
    namespace detail {
        template<std::ranges::input_range R1, std::ranges::input_range R2>
        struct intersection_view : std::ranges::view_interface<intersection_view<R1, R2>> {
            R1 r1_;
            R2 r2_;

            intersection_view(R1 r1, R2 r2) : r1_(std::move(r1)), r2_(std::move(r2)) {}

            struct iterator {
                using value_type = std::common_type_t<std::ranges::range_value_t<R1>, std::ranges::range_value_t<R2>>;
                using iterator_category = std::input_iterator_tag;
                using reference = const value_type&;
                using pointer = const value_type*;

                std::ranges::iterator_t<R1> it1_, end1_;
                std::ranges::iterator_t<R2> it2_, end2_;

                iterator(std::ranges::iterator_t<R1> it1, std::ranges::iterator_t<R1> end1,
                         std::ranges::iterator_t<R2> it2, std::ranges::iterator_t<R2> end2)
                    : it1_(it1), end1_(end1), it2_(it2), end2_(end2) {
                    advance_to_match();
                }

                void advance_to_match() {
                    while (it1_ != end1_ && it2_ != end2_) {
                        if (*it1_ < *it2_) ++it1_;
                        else if (*it2_ < *it1_) ++it2_;
                        else break;
                    }
                }

                reference operator*() const { return *it1_; }
                pointer operator->() const { return std::addressof(*it1_); }

                iterator& operator++() {
                    ++it1_; ++it2_;
                    advance_to_match();
                    return *this;
                }

                iterator operator++(int) {
                    auto tmp = *this;
                    ++*this;
                    return tmp;
                }

                friend bool operator==(const iterator& lhs, const iterator& rhs) {
                    return lhs.it1_ == rhs.it1_ && lhs.it2_ == rhs.it2_;
                }
            };

            iterator begin() {
                return {std::ranges::begin(r1_), std::ranges::end(r1_),
                        std::ranges::begin(r2_), std::ranges::end(r2_)};
            }

            iterator end() {
                return {std::ranges::end(r1_), std::ranges::end(r1_),
                        std::ranges::end(r2_), std::ranges::end(r2_)};
            }
        };
    }

    // 适配器:支持直接调用和管道语法
    inline constexpr auto set_intersection = []<std::ranges::input_range R>(R&& r) {
        struct adaptor {
            R r_;
            explicit adaptor(R&& r) : r_(std::forward<R>(r)) {}

            template<std::ranges::input_range Other>
            friend auto operator|(Other&& other, adaptor&& self) {
                static_assert(std::ranges::sorted<Other>);
                static_assert(std::ranges::sorted<R>);
                return detail::intersection_view(std::forward<Other>(other), std::forward<R>(self.r_));
            }
        };
        return adaptor(std::forward<R>(r));
    };

    // 重载:支持直接传入两个范围
    template<std::ranges::input_range R1, std::ranges::input_range R2>
    auto set_intersection(R1&& r1, R2&& r2) {
        static_assert(std::ranges::sorted<R1>);
        static_assert(std::ranges::sorted<R2>);
        return detail::intersection_view(std::forward<R1>(r1), std::forward<R2>(r2));
    }
}

// 测试代码
#include <iostream>
int main() {
    int a[] {1, 2, 4, 8, 16};
    int b[] {1, 8, 80};

    // 直接调用
    auto i = views::set_intersection(a, b);
    for (int x : i) std::cout << x << ' '; // 输出 1 8
    std::cout << '\n';

    // 管道语法
    auto i_pipe = a | views::set_intersection(b);
    for (int x : i_pipe) std::cout << x << ' '; // 输出 1 8
    std::cout << '\n';

    return 0;
}

3. 扩展到set_union和set_difference

按照相同框架,只需修改迭代器中的advance_to_match和operator++逻辑:

  • set_union:每次输出较小的元素,若元素相等则仅输出一次并同时移动两个迭代器;
  • set_difference:仅输出第一个范围中存在、第二个范围中不存在的元素,逻辑为:第一个元素小于第二个时输出并移动第一个迭代器,相等时同时移动两个迭代器,否则移动第二个迭代器。

三、关键注意事项

  • 必须确保输入范围是已排序的,否则视图行为未定义;
  • 实现需遵循C++20视图规范,保证惰性求值、可复制、满足view概念;
  • 可扩展支持自定义比较器,在适配器中添加比较器参数即可,类似标准库算法的重载。

内容的提问来源于stack exchange,提问作者Jan Schultke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 12:45:23