为何std::ranges::all_of与范围for循环在视图参数使用上存在差异?
我想要按列遍历3x3网格,用std::views::stride实现的代码如下:
#include <array> #include <iostream> #include <ranges> auto main() -> int { auto grid = std::array<std::array<int, 3>, 3>{{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }}; namespace vs = std::views; auto strideCount = grid.size(); auto allElements = grid | vs::join; auto columns = vs::iota(0uz, strideCount) | vs::transform([allElements, strideCount](auto n) { return allElements | vs::drop(n) | vs::stride(strideCount); }); for (auto&& column : columns) { for (auto element : column) { std::cout << element << ' '; } std::cout << '\n'; } }
这段代码能正常按列输出网格元素,但改用std::ranges::all_of验证每列条件时,把lambda参数设为auto&& column会编译报错:
error: passing 'const std::ranges::stride_view<std::ranges::drop_view<std::ranges::join_view<std::ranges::ref_view<std::array<std::array<int, 3>, 3> > > > >' as 'this' argument discards qualifiers [-fpermissive]
将参数改为auto column则编译正常。请问为何范围for循环中可以使用auto&& column,而std::ranges::all_of的lambda参数却不行?使用的编译器是gcc version 13.1.0 (MinGW-W64 x86_64-msvcrt-mcf-seh, built by Brecht Sanders)。
1. 范围for循环的行为
范围for循环遍历columns时,auto&& column绑定的是非const的临时右值:columns是transform生成的视图,每次迭代会产生临时的stride_view对象,auto&&直接绑定这个临时对象。此时对非const的视图调用begin()/end()是合法的,因为这些成员函数不需要const限定。
2. std::ranges::all_of的参数传递逻辑
std::ranges::all_of的设计要求谓词能处理const限定的元素——它会以const引用的方式传递范围元素给lambda(保证只读,避免意外修改)。当你用auto&& column作为lambda参数时,转发引用会绑定到这个const视图对象上。
但stride_view(以及内部嵌套的drop_view、join_view)的begin()/end()成员函数没有const重载,也就是说const限定的视图对象无法调用这些方法来生成迭代器,这就触发了编译报错:试图在const对象上调用非const成员函数,违反了const限定规则。
3. 为什么auto column能正常工作?
用auto column时,会对all_of传递过来的const视图对象进行拷贝,得到一个非const的临时视图。此时调用这个非const视图的begin()/end()是合法的,因此编译通过。视图本身是轻量对象,拷贝成本可以忽略,这是合理的解决方案。
内容的提问来源于stack exchange,提问作者Fureeish

