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;
问题分析
编译失败的核心原因有两点:
prefix_fold_view缺少const限定的begin()/end()成员函数:views::reverse在处理视图时,可能会尝试调用const版本的迭代器获取函数,而当前代码仅实现了非const版本,导致编译器无法匹配到合适的重载。- 闭包类型中函数对象的初始化未使用移动语义:
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
相关产品推荐
相关产品推荐

