The purpose of std::views::common in C++20 Ranges
This situation applies only to C++20 Ranges and their mixing with older STL algorithms such as std::accumulate.
If you stick to views and range algorithms from Eric Niebler's and Casey Carter's Range-v3 library, you will not encounter the issue described below.
Let's look at two cases that differ only in their respective views. The first example uses std::views::filter, the second example uses std::views::take_while. Both are then processed using std::accumulate.
You can compile the examples at Wandbox using the latest GCC as a compiler.
The example with std::views::filter compiles just fine.
#include <iostream> #include <numeric> #include <vector> #include <ranges> // compiles fine int main() { auto v = std::vector{8,7,3}; auto rng = v | std::views::filter([](int x){return x > 5 ;}); auto res = std::accumulate(rng.begin(),rng.end(),0); std::cout << res << '\n'; // 15 }On the other hand, the example with std::views::take_while fails to compile.
#include <iostream> #include <numeric> #include <vector> #include <ranges> // fails to compile int main() { auto v = std::vector{8,7,3}; auto rng = v | std::views::take_while([](int x){return x > 5 ;}); auto res = std::accumulate(rng.begin(),rng.end(),0); std::cout << res << '\n'; // 15 }The reason for this is that unlike std::views::filter, std::views::take_while isn't a common_range. Common ranges require that ranges::begin and ranges::end return the same type.
At cppreference we read that filter_view models the concepts bidirectional_range, forward_range, input_range, and common_range. If we look at the description of take_while_view at cppreference, there is no mention of it being a common_range.
Therefore, to fix the problem for std::views::take_while, we have to add a conversion to std::views::common. The purpose of common_view is to take a C++20 range with a sentinel type different from its iterator type, and adapt it to work with C++17 algorithms, i.e. here std::accumulate, by producing the same iterator and sentinel type.
#include <iostream> #include <numeric> #include <vector> #include <ranges> // compiles fine int main() { auto v = std::vector{8,7,3}; auto rng = v | std::views::take_while([](int x){return x > 5 ;}) | std::views::common; auto res = std::accumulate(rng.begin(),rng.end(),0); std::cout << res << '\n'; // 15 }Now, std::accumulate will process the range rng just fine.
Tagged: C++20 Ranges