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

C++20实现pairwise_filter_view:视图适配与推导指南疑问

实现pairwise_filter_view的范围适配与推导指南

适配规则的技术落地

先明确三条规则的编译期实现逻辑:

  • 输入为视图:直接复用原视图类型,无需额外转换
  • 非可视图化/非前向范围:通过Concepts触发编译报错
  • 前向范围但非视图:自动转为views::all_t<R>,同时拒绝右值临时输入(防止容器销毁导致视图悬空)

步骤1:用Concepts约束合法输入

先定义概念筛选符合要求的输入类型,把非法输入拦截在编译期:

#include <ranges>
#include <concepts>

// 约束:必须是前向范围且可被视图化
template <typename R>
concept ValidPairwiseFilterInput = 
    std::ranges::forward_range<R> && 
    std::ranges::viewable_range<R>;

// 约束:禁止右值临时对象输入(避免悬空指针)
template <typename R>
concept NonRValueInput = 
    !std::is_rvalue_reference_v<R>;

步骤2:实现pairwise_filter适配器与视图类

核心是适配器对象处理输入转换,视图类实现元素过滤逻辑:

// 核心视图类:继承view_interface获得默认begin/end等接口
template <std::ranges::view V, typename Pred>
class pairwise_filter_view : public std::ranges::view_interface<pairwise_filter_view<V, Pred>> {
private:
    V base_;
    Pred pred_;

    // 自定义迭代器:核心逻辑是跳过不满足谓词的连续元素对
    struct iterator {
        using iterator_category = typename std::iterator_traits<typename V::iterator>::iterator_category;
        using value_type = typename std::iterator_traits<typename V::iterator>::value_type;

        typename V::iterator curr_;
        typename V::iterator end_;
        Pred pred_;

        iterator(typename V::iterator curr, typename V::iterator end, Pred pred)
            : curr_(curr), end_(end), pred_(std::move(pred)) {
            // 初始化时跳过首段不满足条件的元素对
            skip_invalid();
        }

        void skip_invalid() {
            while (curr_ != end_) {
                auto next = curr_;
                if (++next == end_) break;
                if (pred_(*curr_, *next)) break;
                ++curr_;
            }
        }

        value_type operator*() const { return *curr_; }
        iterator& operator++() {
            ++curr_;
            skip_invalid();
            return *this;
        }

        bool operator==(const iterator& other) const {
            return curr_ == other.curr_;
        }
    };

public:
    pairwise_filter_view(V base, Pred pred) : base_(std::move(base)), pred_(std::move(pred)) {}

    auto begin() { return iterator{base_.begin(), base_.end(), pred_}; }
    auto end() { return iterator{base_.end(), base_.end(), pred_}; }
};

// 适配器对象:用于管道操作(容器|pairwise_filter(...))
template <typename Pred>
struct pairwise_filter_adaptor {
    Pred pred_;

    // 仅接受符合约束的输入
    template <ValidPairwiseFilterInput R, NonRValueInput R>
    constexpr auto operator()(R&& r) const {
        // 判断输入是否为视图,非视图则转为views::all_t<R>
        using V = std::conditional_t<std::ranges::view<std::remove_cvref_t<R>>,
                                     std::remove_cvref_t<R>,
                                     std::ranges::views::all_t<R>>;
        return pairwise_filter_view<V, Pred>(std::forward<R>(r), pred_);
    }
};

// 工厂函数:简化适配器创建
template <typename Pred>
constexpr auto pairwise_filter(Pred pred) {
    return pairwise_filter_adaptor<Pred>{std::move(pred)};
}

步骤3:添加推导指南自动推导模板参数

推导指南让编译器自动识别输入类型,无需用户手动指定模板参数:

// 通用推导指南:非视图的可视图化范围转为views::all_t<R>
template <std::ranges::viewable_range R, typename Pred>
pairwise_filter_view(R&&, Pred) -> pairwise_filter_view<std::ranges::views::all_t<R>, Pred>;

// 特化推导指南:输入为视图时直接复用原视图类型(优先级高于通用指南)
template <std::ranges::view V, typename Pred>
pairwise_filter_view(V, Pred) -> pairwise_filter_view<V, Pred>;
  • 特化指南优先匹配视图输入,完全符合规则1
  • 通用指南处理普通前向范围,自动转为视图类型,符合规则3

步骤4:编译期错误处理示例

非法输入会触发清晰的编译错误:

// 错误:右值临时容器被NonRValueInput拦截
// auto bad = std::string{"yyxx"} | pairwise_filter([](char a, char b){return !(a=='y'&&b=='y');});

// 错误:输入范围不是前向范围(std::istream_view是输入范围)
// auto bad2 = std::views::istream<char>(std::cin) | pairwise_filter([](char a, char b){return true;});

测试代码

#include <string>
#include <iostream>

int main() {
    std::string s = "yyabyycd";
    // 管道操作:自动将std::string转为views::all_t<std::string>
    auto filtered = s | pairwise_filter([](char a, char b) { return !(a == 'y' && b == 'y'); });
    // 输出:abycd
    for (char c : filtered) {
        std::cout << c;
    }
    std::cout << '\n';

    // 输入为视图:直接复用原视图类型
    auto v = std::views::all(s);
    auto filtered_v = v | pairwise_filter([](char a, char b) { return !(a == 'y' && b == 'y'); });
}

关键细节说明

  1. 推导指南优先级:特化推导指南会优先匹配视图输入,确保视图类型不被额外转换
  2. 悬空指针防护:NonRValueInput概念直接拒绝右值临时容器,从根源避免视图持有已销毁对象的指针
  3. 视图化转换:用std::conditional_t实现类型分支,非视图的前向范围自动转为views::all_t<R>,符合规则3
  4. Concepts约束:所有合法性检查都在编译期完成,错误信息清晰,符合现代C++的设计理念

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 12:52:41