如何用range-v3实现类似ranges::to<T>()的可管道化函数适配multi_linestring
问题解答
一、通用问题:为ranges::to<T>()不支持的类实现类似功能
要让自定义类或第三方库类支持类似ranges::to<T>()的管道化转换,有两种核心方案:
1. 让目标类适配range-v3的容器概念
range-v3的to<T>要求目标类满足Container概念,核心要求包括:
- 支持默认构造、拷贝/移动构造
- 提供
begin()/end()返回合法迭代器 - 支持元素插入(如
push_back()、insert())或范围构造 - 可选:支持
reserve()/resize()优化性能
如果目标类(如Boost.Geometry的multi_linestring)原生不满足,可以:
- 为类添加必要的成员函数(如果有权限修改)
- 特化range-v3的
range::container_traits,手动适配容器接口
2. 实现自定义管道化适配器
如果无法修改目标类,可手动实现一个函数对象,通过重载operator|使其支持管道调用,模仿ranges::to的使用方式。核心步骤:
- 定义一个函数对象,接收range并转换为目标类型
- 重载
operator|,让range视图可以通过管道传递给这个函数对象
二、具体场景:管道化构造Boost.Geometry的multi_linestring
错误原因
ranges::to<polylines>编译失败,是因为Boost.Geometry的multi_linestring没有满足range-v3对容器的全部适配要求,导致to无法自动推导如何从transform_view构造目标容器。
解决方案:将临时函数改造为可管道化适配器
把你现有的to_polylines函数模板,改造成支持管道调用的函数对象:
#include <range/v3/range.hpp> #include <range/v3/view/transform.hpp> namespace r = ranges; namespace rv = ranges::views; namespace bg = boost::geometry; using point = bg::model::point<double, 2, bg::cs::cartesian>; using polyline = bg::model::linestring<point>; using polylines = bg::model::multi_linestring<polyline>; // 定义可管道化的转换函数对象 struct to_polylines_fn { template <typename Rng> polylines operator()(Rng&& rng) const { polylines polys; // 用reserve替代resize,避免默认构造元素,提升效率 polys.reserve(r::distance(rng)); for (auto&& line : rng) { polys.push_back(std::forward<decltype(line)>(line)); } return polys; } // 重载|运算符,支持管道调用 template <typename Rng> friend auto operator|(Rng&& rng, to_polylines_fn) { return to_polylines_fn{}(std::forward<Rng>(rng)); } }; // 全局实例,直接用于管道 inline constexpr to_polylines_fn to_polylines{}; int main() { std::vector<point> some_points = { {1,1},{2,2},{3,3},{4,4},{5,5} }; auto poly = some_points | r::to<polyline>(); std::vector<std::vector<point>> vec_of_vec_of_pts = { {{1,1},{2,2},{3,3},{4,4},{5,5}}, {{6,6},{7,7},{8,8}}, {{9,9},{10,10},{11,11},{12,12}} }; // 现在可以正常使用管道调用 auto polys = vec_of_vec_of_pts | rv::transform([](const auto& v) { return v | r::to<polyline>(); }) | to_polylines; return 0; }
简化写法(用range-v3宏)
如果使用range-v3的宏,可以更简洁地生成适配器:
#include <range/v3/view/adaptor.hpp> // 用宏自动生成适配器和管道支持 RANGES_ADAPTOR_FUNCTION(to_polylines, to_polylines_fn, 1)
这样无需手动重载operator|,宏会自动处理闭包和管道逻辑。
原错误信息
1>[...]: error C2678: binary '|': no operator found which takes a left-hand operand of type 'ranges::transform_view<ranges::ref_view<std::vector<std::vector<point,std::allocator<point>>,std::allocator<std::vector<point,std::allocator<point>>>>>,Arg>' (or there is no acceptable conversion) 1> with 1> [ 1> Arg=main::<lambda_1> 1> ] 1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.29.30133\include\cstddef(42,27): message : could be 'std::byte std::operator |(const std::byte,const std::byte) noexcept' [found using argument-dependent lookup] 1>C:\libraries\range-v3\include\range\v3\view\any_view.hpp(66,24): message : or 'ranges::category ranges::operator |(ranges::category,ranges::category) noexcept' [found using argument-dependent lookup] 1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.29.30133\include\regex(1191,1): message : or 'std::_Node_flags std::operator |(std::_Node_flags,std::_Node_flags) noexcept' [found using argument-dependent lookup] 1>[...]: message : while trying to match the argument list '(ranges::transform_view<ranges::ref_view<std::vector<std::vector<point,std::allocator<point>>,std::allocator<std::vector<point,std::allocator<point>>>>>,Arg>, ranges::detail::to_container::closure<meta::id<polylines>,ranges::detail::to_container::fn<meta::id<polylines>>>)' 1> with 1> [ 1> Arg=main::<lambda_1> 1> ]
内容的提问来源于stack exchange,提问作者jwezorek
相关产品推荐
相关产品推荐

