C++20stladvanced
std::ranges::common_range (C++20 Addition)
C++20 · The Big Four · Compiler flag: -std=c++20
Explanation
Concept for ranges where the iterator and sentinel types are the same. Required for interoperability with legacy algorithms that expect matching begin/end types.
Real-World Scenario
Adapt a sentinel-based range to work with a legacy algorithm that requires identical iterator types.
Quick Snippet
snippet.cpp
#include <ranges>
#include <algorithm>
template<std::ranges::common_range R>
void legacy_sort(R& r) {
std::sort(std::ranges::begin(r), std::ranges::end(r));
}Full Example
ranges_common_range.cpp
// CodeNebula - C++20 Example: std::ranges::common_range Concept
// Compile: g++ -std=c++20 -Wall -Wextra -o example ranges_common_range.cpp
#include <ranges>
#include <vector>
#include <list>
#include <algorithm>
#include <numeric>
#include <iostream>
// std::ranges::common_range requires that begin() and end() return
// the same type. This is important for compatibility with legacy
// algorithms that expect matching iterator types.
// Works only with common ranges (same begin/end type)
template <std::ranges::common_range R>
auto legacy_accumulate(const R& r) {
using T = std::ranges::range_value_t<R>;
return std::accumulate(std::ranges::begin(r), std::ranges::end(r), T{});
}
int main() {
// Standard containers are common ranges
static_assert(std::ranges::common_range<std::vector<int>>);
static_assert(std::ranges::common_range<std::list<int>>);
// Some views are NOT common (begin and end have different types)
auto take5 = std::views::iota(1) | std::views::take(5);
static_assert(!std::ranges::common_range<decltype(take5)>);
// Bounded iota IS common
auto bounded = std::views::iota(1, 6);
static_assert(std::ranges::common_range<decltype(bounded)>);
std::vector<int> nums = {1, 2, 3, 4, 5};
std::cout << "Sum (vector): " << legacy_accumulate(nums) << "\n";
std::cout << "Sum (iota 1..5): " << legacy_accumulate(bounded) << "\n";
// Use common_view to adapt non-common ranges for legacy algorithms
auto non_common = std::views::iota(1) | std::views::take(5);
auto made_common = non_common | std::views::common;
static_assert(std::ranges::common_range<decltype(made_common)>);
// Now we can use legacy algorithms
int sum = std::accumulate(made_common.begin(), made_common.end(), 0);
std::cout << "Sum (made common): " << sum << "\n";
// filter_view on a common range is still common
auto evens = nums | std::views::filter([](int x) { return x % 2 == 0; });
static_assert(std::ranges::common_range<decltype(evens)>);
std::cout << "Sum (evens): " << legacy_accumulate(evens) << "\n";
// Constructing a container from common range iterators
auto transformed = nums
| std::views::transform([](int x) { return x * 10; })
| std::views::common;
std::vector<int> result(transformed.begin(), transformed.end());
std::cout << "Transformed: ";
for (int v : result) std::cout << v << " ";
std::cout << "\n";
return 0;
}
Run command
g++ -std=c++20 -Wall -Wextra -o example ranges_common_range.cpp