std::views::join后接std::views::filter编译失败问题求解
问题原因
这是GCC 10版本中C++20范围库的实现缺陷导致的:
- C++20的范围迭代器新增了
iterator_concept成员标识迭代器实际能力,传统的iterator_category仅用于兼容旧版STL接口。GCC 10的join_view迭代器内部虽然定义了iterator_category成员,但std::iterator_traits没有正确萃取到该成员。 - GCC 10的
filter_view迭代器实现刚好依赖从std::iterator_traits提取iterator_category,因此直接把filter接在join之后会触发编译错误。而transform_view的实现不依赖传统的iterator_category萃取逻辑,所以场景2可以正常编译。
你观察到的iterator_traits无法获取join_view迭代器类型成员的现象,也是同一个实现缺陷导致的。
可行的解决方法
方法1:升级编译器
GCC 11及以上版本已经修复了该范围库实现问题,Clang 13+、MSVC 2022的C++20范围库也可以正常编译你的示例代码。
方法2:兼容GCC 10的代码修改
如果必须使用GCC 10,可以在join和filter之间加一层空的transform适配器,利用transform输出的迭代器可以被iterator_traits正确萃取的特性绕过问题:
#include <ranges> #include <functional> // 用于std::identity int main(int argc_, char* argv_[]) { auto r1 = std::views::single(42); auto r2 = r1 | std::views::transform([](auto& e_) { return std::ranges::single_view{e_}; }); auto r3 = r2 | std::views::join; { // 中间加一层空transform绕过bug auto r4a = r3 | std::views::transform(std::identity{}) | std::views::filter([](auto e_) { return true; }); for (int i : r4a) { } } return 0; }
方法3:转成具体容器再处理
如果数据量不大,也可以先把join后的范围转成std::vector等具体容器,再使用filter适配器:
#include <ranges> #include <vector> // ... auto r3 = r2 | std::views::join | std::ranges::to<std::vector<int>>(); auto r4a = r3 | std::views::filter([](auto e_) { return true; }); // ...
内容的提问来源于stack exchange,提问作者András
相关产品推荐
相关产品推荐

