C++ std::views::filter生成range传入函数编译失败问题
问题现象
OrderProcessor::ProcessRange 函数接收range类型入参并遍历元素。传入std::views::filter构造的range调用该函数时无法编译通过,但如果把ProcessRange内部的循环逻辑直接复制到调用位置编写,代码就能正常编译;如果移除std::views::filter直接传入容器适配的view,代码也可正常编译。
复现代码如下:
#include <iostream> #include <ranges> #include <vector> namespace data { struct Order { }; } namespace app { template <class R, class Value> concept range_over = std::ranges::range<R> && std::same_as<std::ranges::range_value_t<R>, Value>; class OrderProcessor { public: void Process(const data::Order&) {} template <range_over<data::Order> Range> void ProcessRange(const Range& range) { for (const data::Order& order : range) { Process(order); } } }; } int main() { using namespace app; std::vector<data::Order> sample_orders; auto range = sample_orders | std::views::filter([](const data::Order&) -> bool { return true; }); OrderProcessor processor; processor.ProcessRange(range); // 无法编译 // 如下直接编写循环可正常编译: //for (const data::Order& order : range) //{ // processor.Process(order); //} }
GCC 报错信息
prog.cc: In instantiation of 'void app::OrderProcessor::ProcessRange(const Range&) [with Range = std::ranges::filter_view<std::ranges::ref_view<std::vector<data::Order> >, main()::<lambda(const data::Order&)> >]' : prog.cc : 51 : 27 : required from here prog.cc : 32 : 13 : error : passing 'const std::ranges::filter_view<std::ranges::ref_view<std::vector<data::Order> >, main()::<lambda(const data::Order&)> >' as 'this' argument discards qualifiers[-fpermissive] 32 | for (const data::Order& order : range) | ^ ~~ In file included from prog.cc : 2 : / opt / wandbox / gcc - 11.1.0 / include / c++ / 11.1.0 / ranges : 1307 : 7 : note : in call to 'constexpr std::ranges::filter_view<_Vp, _Pred>::_Iterator std::ranges::filter_view<_Vp, _Pred>::begin() [with _Vp = std::ranges::ref_view<std::vector<data::Order> >; _Pred = main()::<lambda(const data::Order&)>]' 1307 | begin() | ^ ~~~~ prog.cc : 32 : 13 : error : passing 'const std::ranges::filter_view<std::ranges::ref_view<std::vector<data::Order> >, main()::<lambda(const data::Order&)> >' as 'this' argument discards qualifiers[-fpermissive] 32 | for (const data::Order& order : range) | ^ ~~ In file included from prog.cc : 2 : / opt / wandbox / gcc - 11.1.0 / include / c++ / 11.1.0 / ranges : 1321 : 7 : note : in call to 'constexpr auto std::ranges::filter_view<_Vp, _Pred>::end() [with _Vp = std::ranges::ref_view<std::vector<data::Order> >; _Pred = main()::<lambda(const data::Order&)>]' 1321 | end() | ^ ~~
问题根因
报错核心是尝试在const限定的std::ranges::filter_view对象上调用非const的begin()/end()方法,具体逻辑拆解:
ProcessRange的入参是const Range&,函数体内遍历range时,操作的是const限定的range对象,会调用该对象的begin()和end()方法,要求这两个方法必须有const重载。std::ranges::filter_view的begin()、end()没有提供const重载:filter视图为了避免每次调用begin()都从头遍历底层容器找第一个符合条件的元素,会在第一次调用begin()时把找到的迭代器缓存到视图内部,这个操作会修改视图自身的成员状态,因此begin()不能声明为const成员函数,自然无法在const的filter_view对象上调用。- 直接把循环写在main函数中时,局部变量
range是非const对象,调用非const的begin()/end()完全合法,因此不会报错。 - 移除filter直接传入vector或
ref_view时,这两类类型的begin()/end()都提供了const重载,const对象上也可以正常调用遍历,因此也不会报错。 - 额外设计缺陷:自定义的
range_over概念只校验了非const的Range类型满足std::ranges::range要求,没有校验const限定后的Range是否也能作为range遍历,因此没有在模板约束阶段拦下不合法的参数类型,直到函数体实例化时才触发编译错误。
修复方案
- 不要将range入参声明为const左值引用,改用转发引用
Range&&接收range,适配仅支持非const遍历的视图类型; - 如果确实需要接收const的range,需要把概念校验改为检查
const Range也满足range要求,提前拦截不合法的参数类型。
内容的提问来源于stack exchange,提问作者Dmitriano
相关产品推荐
相关产品推荐

