Fur*_*ish 6 c++ for-loop std-ranges c++23
我想逐列查看 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';
}
}
Run Code Online (Sandbox Code Playgroud)
这可以工作并打印:
1 4 7
2 5 8
3 6 9
Run Code Online (Sandbox Code Playgroud)
也就是说,它逐列打印每个元素。
在我最初的示例中,我想测试每列是否满足给定的标准。for ()
我尝试使用:而不是基于范围的循环std::ranges::all_of()
:
1 4 7
2 5 8
3 6 9
Run Code Online (Sandbox Code Playgroud)
唯一的区别是:
#include <algorithm>
#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);
});
std::ranges::all_of(columns, [](auto&& column) {
for (auto element : column) {
// logic that alters whether we return true or false
}
return true;
});
}
Run Code Online (Sandbox Code Playgroud)
我现在有:
for (auto&& column : columns) {
for (auto element : column) {
std::cout << element << ' ';
}
std::cout << '\n';
}
Run Code Online (Sandbox Code Playgroud)
有了适当的#include <algorithm>
.
然而,不幸的是这不起作用。它在这里失败(在std::ranges::all_of()
片段中):
std::ranges::all_of(columns, [](auto&& column) {
for (auto element : column) {
// logic that alters whether we return true or false
}
return true;
});
Run Code Online (Sandbox Code Playgroud)
错误地声称:
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]
Run Code Online (Sandbox Code Playgroud)
对于两者begin()
和end()
.
但是,如果我不接受auto&& column
lambda auto column
,代码就会编译。
为什么会这样呢?为什么我可以对循环示例中的每一列使用转发引用for ()
,但在某些情况下不能在 lambda 参数中执行此操作std::ranges::algorithm
?
我正在使用gcc version 13.1.0 (MinGW-W64 x86_64-msvcrt-mcf-seh, built by Brecht Sanders)
(的输出g++ -v
)。
ranges::all_of
要求谓词Pred
满足indirect_unary_predicate<Pred, projected<I, Proj>>
,最终导致
invocable<Pred&, iter_common_reference_t<projected<I, Proj>>>
Run Code Online (Sandbox Code Playgroud)
必须满足,其中Proj
是投影函数。
类模板是一种辅助类型,用于构造新的迭代器类型,其引用类型是(在大多数情况下)std::projected
应用于 的结果,这有助于拼写“范围化”算法的要求([投影]):Proj
std::identity
iter_reference_t<I>
namespace std {
template<class I, class Proj>
struct projected-impl { // exposition only
struct type { // exposition only
using value_type = remove_cvref_t<indirect_result_t<Proj&, I>>;
using difference_type = iter_difference_t<I>; // present only if I
// models weakly_incrementable
indirect_result_t<Proj&, I> operator*() const; // not defined
};
};
template<indirectly_readable I, indirectly_regular_unary_invocable<I> Proj>
using projected = projected-impl<I, Proj>::type;
}
Run Code Online (Sandbox Code Playgroud)
让我们回到invocable<Pred&, iter_common_reference_t<projected<I, Proj>>>
.
在您的示例中,原始迭代器I
和reference
都是value_type
prvalue stride_view
,因此其公共引用iter_common_reference_t<I>
也是stride_view
。
然而,projected<I, Proj>
和分别reference
是value_type
和,这使得最终stride_view&&
的计算stride_view
结果为。由于是 不可迭代的,这会导致实例化期间出现硬错误,因为我们在不受约束的 lambda 内部调用's 。iter_common_reference_t<projected<I, Proj>>
const stride_view&
stride_view
const
const stride_view
begin
这是一个标准缺陷。看来我们仍然需要 LWG 3859projected<I, std::identity>
的拟议决议来制定I
,尽管它被标记为“由P2609R3解决”,但本例并非如此。
解决方法是使用identity
返回纯右值的虚拟对象,以便投影迭代器的公共引用仍然是纯右值(demo):
std::ranges::all_of(columns, [](auto&& column) {
for (auto element : column) {
// logic that alters whether we return true or false
}
return true;
}, [](auto r) { return r; });
Run Code Online (Sandbox Code Playgroud)