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

C++20自定义range无法被views::reverse反转的问题排查

自定义prefix_fold_view无法被views::reverse反转的问题修复

我在学习std::ranges时实现了一个前缀折叠视图prefix_fold_view,内部用vector存储计算结果,返回的迭代器属于随机访问类型,但尝试用views::reverse反转该视图时编译失败,尤其是在suffix_fold的实现中嵌套调用views::reverse时。移除views::reverse调用后代码可正常编译。

原代码如下:

template<ranges::range R, typename T> requires ranges::view<R> 
struct prefix_fold_view : public ranges::view_interface<prefix_fold_view<R, T>>{
    vector<T> __prefix_fold;
    prefix_fold_view(R base, auto&& f, T initial){
        __prefix_fold.resize(ranges::size(base) + 1);
        __prefix_fold[0] = initial;
        auto iter_rg = base.begin();
        auto iter_fold = __prefix_fold.begin();
        while(iter_rg != base.end()){
            *(++iter_fold) = f(*iter_fold, *iter_rg);
            ++iter_rg;
        }
    }
    auto begin(){
        return __prefix_fold.cbegin();
    }
    auto end(){
        return __prefix_fold.cend();
    }
};
template<typename T, typename F>
struct prefix_fold_range_adapter_closure{
    T initial;
    F f;
    template<ranges::viewable_range R>
    constexpr auto operator()(R&& r){
        return prefix_fold_view<ranges::views::all_t<R>, T>(r, std::move(f), initial);
    }
    prefix_fold_range_adapter_closure(F&& _f, T _initial) : f(_f), initial(_initial) {}
};
struct prefix_fold_range_adapter{
    template<ranges::viewable_range R>
    constexpr auto operator()(R&& r, auto&& f, auto initial) const{
        return prefix_fold_view<ranges::views::all_t<R>, decltype(initial)>(r, std::move(f), initial);
    }
    constexpr auto operator()(auto &&f, auto initial) const{
        return prefix_fold_range_adapter_closure(std::forward<decltype(f)>(f), initial);
    }
};
template <ranges::viewable_range R, typename T, typename F>
constexpr auto operator | (R&& r, prefix_fold_range_adapter_closure<T, F>&& closure){
    return closure(std::forward<R>(r));
}
prefix_fold_range_adapter prefix_fold;
template<typename T, typename F>
struct suffix_fold_range_adapter_closure{
    T initial;
    F f;
    template<ranges::viewable_range R>
    constexpr auto operator()(R&& r){
        auto rev = views::reverse(r);
        return views::reverse(prefix_fold_view<ranges::views::all_t<decltype(rev)>, T>(rev, std::move(f), initial));
    }
    suffix_fold_range_adapter_closure(F&& _f, T _initial) : f(_f), initial(_initial) {}
};
struct suffix_fold_range_adapter{
    template<ranges::viewable_range R>
    constexpr auto operator()(R&& r, auto&& f, auto initial) const{
        auto rev = views::reverse(r);
        return views::reverse(prefix_fold_view<ranges::views::all_t<decltype(rev)>, decltype(initial)>(rev, std::move(f), initial));
    }
    constexpr auto operator()(auto &&f, auto initial) const{
        return suffix_fold_range_adapter_closure(std::move(f), initial);
    }
};
suffix_fold_range_adapter suffix_fold;

问题分析

编译失败的核心原因有两点:

  1. prefix_fold_view缺少const限定的begin()/end()成员函数:views::reverse在处理视图时,可能会尝试调用const版本的迭代器获取函数,而当前代码仅实现了非const版本,导致编译器无法匹配到合适的重载。
  2. 闭包类型中函数对象的初始化未使用移动语义:prefix_fold_range_adapter_closure和suffix_fold_range_adapter_closure的构造函数中,直接用右值引用参数赋值给成员变量,没有移动,可能导致不必要的复制,甚至在某些情况下引发编译错误(比如函数对象不可复制时)。

修复后代码

#include <ranges>
#include <vector>
#include <iterator>

template<ranges::range R, typename T> requires ranges::view<R> 
struct prefix_fold_view : public ranges::view_interface<prefix_fold_view<R, T>>{
    std::vector<T> __prefix_fold;

    // 改为constexpr构造函数,支持constexpr上下文
    constexpr prefix_fold_view(R base, auto&& f, T initial){
        __prefix_fold.resize(ranges::size(base) + 1);
        __prefix_fold[0] = std::move(initial); // 移动initial避免复制
        auto iter_rg = base.begin();
        auto iter_fold = __prefix_fold.begin();
        while(iter_rg != base.end()){
            *(++iter_fold) = f(*iter_fold, *iter_rg);
            ++iter_rg;
        }
    }

    // 添加const限定的begin/end,满足const上下文的迭代器获取需求
    constexpr auto begin() const {
        return __prefix_fold.cbegin();
    }

    constexpr auto end() const {
        return __prefix_fold.cend();
    }

    // 保留非const版本,行为与const版本一致
    constexpr auto begin() {
        return __prefix_fold.cbegin();
    }

    constexpr auto end() {
        return __prefix_fold.cend();
    }
};

template<typename T, typename F>
struct prefix_fold_range_adapter_closure{
    T initial;
    F f;

    template<ranges::viewable_range R>
    constexpr auto operator()(R&& r){
        return prefix_fold_view<ranges::views::all_t<R>, T>(std::forward<R>(r), std::move(f), std::move(initial));
    }

    // 移动初始化函数对象,避免不必要的复制
    prefix_fold_range_adapter_closure(F&& _f, T _initial) : f(std::move(_f)), initial(std::move(_initial)) {}
};

struct prefix_fold_range_adapter{
    template<ranges::viewable_range R>
    constexpr auto operator()(R&& r, auto&& f, auto initial) const{
        return prefix_fold_view<ranges::views::all_t<R>, decltype(initial)>(
            std::forward<R>(r), 
            std::forward<decltype(f)>(f), 
            std::move(initial)
        );
    }

    constexpr auto operator()(auto &&f, auto initial) const{
        return prefix_fold_range_adapter_closure(std::forward<decltype(f)>(f), std::move(initial));
    }
};

template <ranges::viewable_range R, typename T, typename F>
constexpr auto operator | (R&& r, prefix_fold_range_adapter_closure<T, F>&& closure){
    return closure(std::forward<R>(r));
}

prefix_fold_range_adapter prefix_fold;

template<typename T, typename F>
struct suffix_fold_range_adapter_closure{
    T initial;
    F f;

    template<ranges::viewable_range R>
    constexpr auto operator()(R&& r){
        auto rev = views::reverse(std::forward<R>(r));
        return views::reverse(
            prefix_fold_view<ranges::views::all_t<decltype(rev)>, T>(
                std::move(rev), 
                std::move(f), 
                std::move(initial)
            )
        );
    }

    // 移动初始化函数对象
    suffix_fold_range_adapter_closure(F&& _f, T _initial) : f(std::move(_f)), initial(std::move(_initial)) {}
};

struct suffix_fold_range_adapter{
    template<ranges::viewable_range R>
    constexpr auto operator()(R&& r, auto&& f, auto initial) const{
        auto rev = views::reverse(std::forward<R>(r));
        return views::reverse(
            prefix_fold_view<ranges::views::all_t<decltype(rev)>, decltype(initial)>(
                std::move(rev), 
                std::forward<decltype(f)>(f), 
                std::move(initial)
            )
        );
    }

    constexpr auto operator()(auto &&f, auto initial) const{
        return suffix_fold_range_adapter_closure(std::forward<decltype(f)>(f), std::move(initial));
    }
};

suffix_fold_range_adapter suffix_fold;

修复说明

  • 添加const限定的begin()/end()后,prefix_fold_view可以同时支持const和非const上下文的迭代器获取,满足views::reverse对可反转范围的要求。
  • 对构造函数中的initial和函数对象f使用移动语义,减少不必要的复制,提升性能,同时避免函数对象不可复制时的编译错误。
  • 将构造函数和迭代器函数改为constexpr,让视图支持constexpr场景的使用,符合标准库视图的设计习惯。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 10:20:39