Alb*_*ini 3 c++ c++20 std-ranges c++23
此代码可以正确编译并运行:
\n#include <ranges>\n#include <iostream>\n\nint main() {\n const auto r = std::views::iota(\'a\', static_cast<char>(\'g\' + 1));\n\n for(const auto& [start, end] : r | std::views::chunk(3u)) {\n for(auto it = start; it != end; ++it) {\n std::cout << *it << " ";\n }\n std::cout << "\\n";\n }\n\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n其输出为:
\na b c \nd e f \ng \nRun Code Online (Sandbox Code Playgroud)\n如果我将定义更改r如下:
a b c \nd e f \ng \nRun Code Online (Sandbox Code Playgroud)\n该代码无法编译。GCC 13 发出以下错误:
\nchunk.cpp:13:21: error: cannot decompose inaccessible member \xe2\x80\x98std::ranges::take_view<std::ranges::subrange<std::ranges::iota_view<char, int>::_Iterator, std::ranges::iota_view<char, int>::_Sentinel, std::ranges::subrange_kind::unsized> >::_M_base\xe2\x80\x99 of \xe2\x80\x98const std::ranges::take_view<std::ranges::subrange<std::ranges::iota_view<char, int>::_Iterator, std::ranges::iota_view<char, int>::_Sentinel, std::ranges::subrange_kind::unsized> >\xe2\x80\x99\n 13 | for(const auto& [start, end] : r | std::views::chunk(3u)) {\n | ^~~~~~~~~~~~\nIn file included from chunk.cpp:1:\n/usr/include/c++/13/ranges:2153:11: note: declared private here\n 2153 | _Vp _M_base = _Vp();\n | ^~~~~~~\nRun Code Online (Sandbox Code Playgroud)\n我认为这\'g\' + 1导致了整数提升,并使第一个和第二个参数具有iota()不同的类型。然而,在其他情况下,这似乎很好。例如,此代码按预期工作:
#include <ranges>\n#include <iostream>\n\nint main() {\n const auto r = std::views::iota(\'a\', \'g\' + 1);\n\n for(const auto& val : r) {\n std::cout << val << " ";\n }\n std::cout << "\\n";\n\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n这里发生了什么?为什么错误提到(所有的)私人成员?
\n由于'g' + 1产生整数提升,这使得views::iota('a', 'g' + 1)产生iota_view迭代器类型与哨兵类型不同的迭代器类型。
根据[range.iota.sentinel],哨兵类型只能在满足时从迭代器类型中减去sized_sentinel_for<W, Bound>,而基本类型则不然char,int因为它们不是迭代器。
换句话说,这破坏了iota_view的哨兵和迭代器减法功能:
auto r = views::iota('a', 'g' + 1);
r.begin() - r.end(); // error
Run Code Online (Sandbox Code Playgroud)
当应用views::chunck它时,结果值类型将是decltype(views::take(subrange(current_, end_), n_))([range.chunk.fwd.iter]),其中current_和end_是 this 的迭代器和哨兵iota_view。
由于current_并且end_不再模型sized_sentinel_for,CTAD 将subrange使用第三个模板参数 ( subrange_kind) 推导出 a 是unsized( [range.subrange.general] ),并且这样的 asubrange不再是sized_range:
auto r = views::iota('a', 'g' + 1);
auto s = ranges::subrange(r.begin(), r.end());
static_assert(ranges::sized_range<decltype(s)>); // failed
Run Code Online (Sandbox Code Playgroud)
这使得views::take返回一个take_view对象([range.take.overview#2.5]),该对象不能进行结构化绑定。
对于您的原始示例,由于 的迭代器类型views::iota('a', 'g')与哨兵类型相同,并且两者可以相减,subrange(current_, end_)因此仍将建模sized_range和random_access_range。在这种情况下,views::take将最佳地返回subrange可以结构化绑定的类型([range.take.overview#2.2.3])。
这可以说是LWG 3609标准中的一个缺陷。