size() causes error when switching from transform to filter

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

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].

<!---->

#include &lt;iostream&gt;
#include &lt;ranges&gt;
#include &lt;sstream&gt;

int main()
{
    using namespace std;
    auto input = views::iota(1, 5 + 1);

    auto output = input |
                  views::filter([](auto n) { return n % 2 == 0; });
                  //views::transform([](auto n) { return 2 * n; });


    stringstream ss;
    ss &lt;&lt; &quot;[&quot;;
    for (auto i = 0; i &lt; output.size() - 1; ++i)
    {
        ss &lt;&lt; output[i] &lt;&lt; &#39;,&#39;;
    }
    ss &lt;&lt; output[output.size() - 1];
    ss &lt;&lt; &quot;]&quot;;
    cout &lt;&lt; ss.str();
    return 0;
}

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 中:

auto output_view = input
            | views::filter([](auto n) { return n % 2 == 0; }) 
            | views::transform([](auto n) { return 2 * n; });

std::vector<int> output(ranges::begin(output_view), ranges::end(output_view));

在 C++23 中:

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

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

stringstream ss;
ss << "[";
auto it = ranges::begin(output);
for (; ranges::next(it) != ranges::end(output); ++it) {
    ss << *it << ',';
}
ss << *it;
ss << "]";
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:

auto output_view = input
            | views::filter([](auto n) { return n % 2 == 0; }) 
            | views::transform([](auto n) { return 2 * n; });

std::vector&lt;int&gt; output(ranges::begin(output_view), ranges::end(output_view));

In C++23:

auto output = input
            | views::filter([](auto n) { return n % 2 == 0; }) 
            | views::transform([](auto n) { return 2 * n; })
            | 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:

stringstream ss;
ss &lt;&lt; &quot;[&quot;;
auto it = ranges::begin(output);
for (; ranges::next(it) != ranges::end(output); ++it) {
    ss &lt;&lt; *it &lt;&lt; &#39;,&#39;;
}
ss &lt;&lt; *it;
ss &lt;&lt; &quot;]&quot;;
cout &lt;&lt; ss.str();

答案2

得分: 5

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

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

const char *sep = "";
for (const auto &n : output)
{
    ss << sep << n;
    sep = ",";
}
英文:

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:

    const char *sep=&quot;&quot;;
    for (const auto &amp;n:output)
    {
	    ss &lt;&lt; sep &lt;&lt; n;
	    sep=&quot;,&quot;;
    }

答案3

得分: 4

#include <iostream>
#include <ranges>
#include <sstream>

int main()
{
    using namespace std;
    auto input = views::iota(1, 5 + 1);
    
    auto output = input | views::filter([](const auto& n) {return n % 2 == 0; });
    
    stringstream ss;
    ss << "[";
    if (not ranges::empty(output))
    {
        ss << *ranges::begin(output);
        for (const auto &x : output | views::drop(1)) 
            ss << ',' << x;
    }
    ss << "]";
    cout << ss.str();
    return 0;
}
英文:
#include &lt;iostream&gt;
#include &lt;ranges&gt;
#include &lt;sstream&gt;


int main()
{
    using namespace std;
    auto input = views::iota(1, 5 + 1);

    auto output = input | views::filter([](const auto&amp; n) {return n % 2 == 0; });

    stringstream ss;
    ss &lt;&lt; &quot;[&quot;;
    if (not ranges::empty(output))
    {
        ss &lt;&lt; *ranges::begin(output);
        for (const auto &amp;x : output | views::drop(1)) 
            ss &lt;&lt; &#39;,&#39; &lt;&lt; x;
    }
    ss &lt;&lt; &quot;]&quot;;
    cout &lt;&lt; ss.str();
    return 0;
}

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:

确定