size() causes error when switching from transform to filter

huangapple go评论106阅读模式
英文:

size() causes error when switching from transform to filter

问题

When views::filter([](auto n) { return n % 2 == 0; }); is activated, I fail to get [2,4]. When views::transform([](auto n) { return 2 * n; }); is activated, I succeed to get [2,4,6,8,10].

I don't understand the following error:

Filtering.cpp:17:37: error: no matching function for call to 'std::ranges::filter_view<std::ranges::iota_view<int, int>, main()::<lambda(auto:17)>>::size()'

英文:

When

  • views::filter([](auto n) { return n % 2 == 0; }); is activated, I fail to get [2,4].
  • views::transform([](auto n) { return 2 * n; }); is activated, I succeed to get [2,4,6,8,10].

<!---->

  1. #include &lt;iostream&gt;
  2. #include &lt;ranges&gt;
  3. #include &lt;sstream&gt;
  4. int main()
  5. {
  6. using namespace std;
  7. auto input = views::iota(1, 5 + 1);
  8. auto output = input |
  9. views::filter([](auto n) { return n % 2 == 0; });
  10. //views::transform([](auto n) { return 2 * n; });
  11. stringstream ss;
  12. ss &lt;&lt; &quot;[&quot;;
  13. for (auto i = 0; i &lt; output.size() - 1; ++i)
  14. {
  15. ss &lt;&lt; output[i] &lt;&lt; &#39;,&#39;;
  16. }
  17. ss &lt;&lt; output[output.size() - 1];
  18. ss &lt;&lt; &quot;]&quot;;
  19. cout &lt;&lt; ss.str();
  20. return 0;
  21. }

I don't understand the following error.

> Filtering.cpp:17:37: error: no matching function for call to 'std::ranges::filter_view<std::ranges::iota_view<int, int>, main()::<lambda(auto:17)> >::size()'
17 | for (auto i = 0; i < output.size() - 1; ++i)

答案1

得分: 7

std::ranges::filter_view 不是一个 "有大小的范围",这意味着您无法在它上面调用 size。这是因为过滤是按需进行的,因此要求查看的大小需要消耗每个元素。

一种选择是将视图转换为 std::vector 并使用它。在 C++20 中:

  1. auto output_view = input
  2. | views::filter([](auto n) { return n % 2 == 0; })
  3. | views::transform([](auto n) { return 2 * n; });
  4. std::vector<int> output(ranges::begin(output_view), ranges::end(output_view));

在 C++23 中:

  1. auto output = input
  2. | views::filter([](auto n) { return n % 2 == 0; })
  3. | views::transform([](auto n) { return 2 * n; })
  4. | ranges::to<std::vector>();

另一种选择是利用 filter_view 是一个前向范围的事实,只要底层范围是前向的,您就可以检查是否将迭代器前进到末尾:

  1. stringstream ss;
  2. ss << "[";
  3. auto it = ranges::begin(output);
  4. for (; ranges::next(it) != ranges::end(output); ++it) {
  5. ss << *it << ',';
  6. }
  7. ss << *it;
  8. ss << "]";
  9. cout << ss.str();
英文:

std::ranges::filter_view is not a "sized range", which means you can't call size on it. This is because the filtering is done on-demand, so asking for the size of the view would require consuming every element.

One option is to convert the view into a std::vector and use that. In C++20:

  1. auto output_view = input
  2. | views::filter([](auto n) { return n % 2 == 0; })
  3. | views::transform([](auto n) { return 2 * n; });
  4. std::vector&lt;int&gt; output(ranges::begin(output_view), ranges::end(output_view));

In C++23:

  1. auto output = input
  2. | views::filter([](auto n) { return n % 2 == 0; })
  3. | views::transform([](auto n) { return 2 * n; })
  4. | ranges::to&lt;std::vector&gt;();

Another option is to make use of the fact that filter_view is a forward range so long as the underlying range is, so you can check if advancing the iterator hits the end:

  1. stringstream ss;
  2. ss &lt;&lt; &quot;[&quot;;
  3. auto it = ranges::begin(output);
  4. for (; ranges::next(it) != ranges::end(output); ++it) {
  5. ss &lt;&lt; *it &lt;&lt; &#39;,&#39;;
  6. }
  7. ss &lt;&lt; *it;
  8. ss &lt;&lt; &quot;]&quot;;
  9. cout &lt;&lt; ss.str();

答案2

得分: 5

filter_view不是一个random_access_range,它没有size()方法。如果您专注于您展示的确切错误消息,它确实如此:在附近没有size()

您可以将其类比为std::forward_list,而不是std::vector。您可以使用范围迭代,如下所示:

  1. const char *sep = "";
  2. for (const auto &n : output)
  3. {
  4. ss << sep << n;
  5. sep = ",";
  6. }
英文:

A filter_view is not a random_access_range and it does not have a size() method. If you focus your attention on the exact error message that you showed that's what it says: there is no size(), anywhere in the vicinity.

You can think of it as analogous to a std::forward_list, instead of std::vector. You can use range iteration, instead:

  1. const char *sep=&quot;&quot;;
  2. for (const auto &amp;n:output)
  3. {
  4. ss &lt;&lt; sep &lt;&lt; n;
  5. sep=&quot;,&quot;;
  6. }

答案3

得分: 4

  1. #include <iostream>
  2. #include <ranges>
  3. #include <sstream>
  4. int main()
  5. {
  6. using namespace std;
  7. auto input = views::iota(1, 5 + 1);
  8. auto output = input | views::filter([](const auto& n) {return n % 2 == 0; });
  9. stringstream ss;
  10. ss << "[";
  11. if (not ranges::empty(output))
  12. {
  13. ss << *ranges::begin(output);
  14. for (const auto &x : output | views::drop(1))
  15. ss << ',' << x;
  16. }
  17. ss << "]";
  18. cout << ss.str();
  19. return 0;
  20. }
英文:
  1. #include &lt;iostream&gt;
  2. #include &lt;ranges&gt;
  3. #include &lt;sstream&gt;
  4. int main()
  5. {
  6. using namespace std;
  7. auto input = views::iota(1, 5 + 1);
  8. auto output = input | views::filter([](const auto&amp; n) {return n % 2 == 0; });
  9. stringstream ss;
  10. ss &lt;&lt; &quot;[&quot;;
  11. if (not ranges::empty(output))
  12. {
  13. ss &lt;&lt; *ranges::begin(output);
  14. for (const auto &amp;x : output | views::drop(1))
  15. ss &lt;&lt; &#39;,&#39; &lt;&lt; x;
  16. }
  17. ss &lt;&lt; &quot;]&quot;;
  18. cout &lt;&lt; ss.str();
  19. return 0;
  20. }

huangapple
  • 本文由 发表于 2023年4月13日 18:40:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76004478.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定