Some C++20 ranges views not compatible with std::copy

198 views Asked by At

In the code below, as it stands, doesn't compile. But replace std::views::take(2) with std::views::drop_while and the code compile. I do understand that the take_view and drop_while view do different things, non the less, it shouldn't matter if I want to take some or drop some, I still should be able to copy those elems into the container.

#include <ranges>
#include <vector>
#include <iostream>
#include <unordered_map>
using namespace std;

int main()
{
    //std::views::drop_while([](const int val){return val < 9;}) |

    vector v{1,2,3,4,5,6,7,4,8,9};   
    std::ranges::take_view res = v | std::views::filter([](const int val){return val %2 == 0;}) | std::views::transform([](const int val){return val*3;}) 
    |  std::views::take(2);
    decltype(v) filtered;
    std::copy(res.begin(),res.end(),std::back_inserter(filtered));
    for (const auto val : filtered)
    {
        std::cout << "val: " << val << '\n';
    }


    return 0;
}

Tried to look for explanation on the net. EDIT: Error:

main.cpp: In function ‘int main()’: main.cpp:23:14: error: no matching function for call to ‘copy(std::counted_iterator > >, main():: >, main():: >::_Iterator >, std::ranges::take_view > >, main():: >, main():: > >::_Sentinel, std::back_insert_iterator > >)’ 23 |
std::copy(res.begin(),res.end(),std::back_inserter(filtered)); | ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from /usr/include/c++/11/bits/char_traits.h:39, from /usr/include/c++/11/string:40, from /usr/include/c++/11/bits/locale_classes.h:40, from /usr/include/c++/11/bits/ios_base.h:41, from /usr/include/c++/11/streambuf:41, from /usr/include/c++/11/bits/streambuf_iterator.h:35, from /usr/include/c++/11/iterator:66, from /usr/include/c++/11/ranges:43, from main.cpp:9: /usr/include/c++/11/bits/stl_algobase.h:611:5: note: candidate: ‘template constexpr _OI std::copy(_II, _II, _OI)’ 611 | copy(_II __first, _II __last, _OI __result) | ^~~~ /usr/include/c++/11/bits/stl_algobase.h:611:5: note: template argument deduction/substitution failed: main.cpp:23:14: note: deduced conflicting types for parameter ‘_II’ (‘std::counted_iterator

, main():: >, main():: >::_Iterator >’ and ‘std::ranges::take_view > >, main():: >, main():: > >::_Sentinel’) 23 | std::copy(res.begin(),res.end(),std::back_inserter(filtered)); | ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from /usr/include/c++/11/iterator:66, from /usr/include/c++/11/ranges:43, from main.cpp:9: /usr/include/c++/11/bits/streambuf_iterator.h:325:5: note: candidate: ‘template typename __gnu_cxx::__enable_if::__value, std::ostreambuf_iterator<_CharT> >::__type std::copy(std::istreambuf_iterator<_CharT>, std::istreambuf_iterator<_CharT>, std::ostreambuf_iterator<_CharT>)’
325 | copy(istreambuf_iterator<_CharT> __first, | ^~~~ /usr/include/c++/11/bits/streambuf_iterator.h:325:5: note: template argument deduction/substitution failed: main.cpp:23:14: note: ‘std::counted_iterator > >, main():: >, main():: >::_Iterator >’ is not derived from ‘std::istreambuf_iterator<_CharT>’ 23 |
std::copy(res.begin(),res.end(),std::back_inserter(filtered)); | ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1

There are 1 answers

0
Toby Speight On BEST ANSWER

The range res doesn't model Common Range, i.e. its begin() and end() have different types.

The easy solution is to simply use the range version of the algorithm instead:

    std::ranges::copy(res, std::back_inserter(filtered));

If you really need start and end (for a function such as std::accumulate() which is not yet rangeified), then we'll need to convert to a common range.

    auto common_res = res | std::views::common;
    std::copy(common_res.begin(), common_res.end(), std::back_inserter(filtered));

Full program:

#include <algorithm>
#include <iostream>
#include <ranges>
#include <vector>

int main()
{
    std::ranges::take_view res = std::views::iota(1,10)
        | std::views::filter([](const int val){return val %2 == 0;})
        | std::views::transform([](const int val){return val*3;})
        | std::views::take(2);

    std::vector<int> filtered;
    std::ranges::copy(res, std::back_inserter(filtered));
    for (const auto val : filtered) {
        std::cout << "val: " << val << '\n';
    }
}