C++ PROGRAMMING ยท 11 MIN STUDY

C++20 concepts and ranges

Use concepts to constrain templates and ranges to express algorithms over sequences more clearly.

Learning objectives

  • Explain why template constraints improve diagnostics and interfaces.
  • Use a standard concept such as std::integral.
  • Understand the C++20 ranges library and range-based algorithms.
  • Use a simple view pipeline.

Prerequisites: understand templates, iterators, std::vector, and lambda expressions. Review Templates, Iterator categories, Vectors, and Lambdas first. Examples require a compiler and standard library with C++20 ranges support.

Why concepts?

Templates allow a function to work with many types. Without constraints, an invalid type may produce a long error deep inside a template. A concept names a set of requirements a type must satisfy. It makes the intended interface clearer and can reject unsuitable types earlier.

#include <concepts>
#include <iostream>
template<std::integral T>
T add_one(T value) {
    return value + 1;
}
int main() {
    std::cout << add_one(41) << '\n';
}

std::integral is a standard concept in <concepts>. It accepts integral types such as int and rejects types such as std::string. Concepts constrain what a template accepts; they do not automatically make every operation safe from overflow.

requires expressions

template<typename T>
requires requires(T item) { item.size(); }
auto length_of(const T& value) {
    return value.size();
}

The outer requires clause constrains the template. The inner requires-expression checks whether the expression item.size() is valid for T.

Ranges algorithms

#include <algorithm>
#include <ranges>
#include <vector>
std::vector<int> values{8, 1, 6, 3};
std::ranges::sort(values);
auto found = std::ranges::find(values, 6);

Ranges algorithms can accept a range directly rather than requiring a begin/end pair. They still obey the requirements of the algorithm: sorting needs a suitable sortable range.

Views and pipelines

#include <iostream>
#include <ranges>
auto result = values
    | std::views::filter([](int x) { return x % 2 == 0; })
    | std::views::transform([](int x) { return x * x; });
for (int x : result) std::cout << x << ' ';

A view is generally a lightweight, non-owning description of elements or a transformation. The source range must remain alive while the view is used. Many views are lazy: the transformation happens as elements are traversed rather than creating a new vector immediately.

Compile with C++20

g++ -std=c++20 -Wall -Wextra -pedantic main.cpp -o app

Common mistakes

  • Forgetting -std=c++20 or equivalent compiler configuration.
  • Assuming a view owns the source data.
  • Using a range after its source container has been destroyed.
  • Thinking concepts validate runtime values; they constrain types and expressions at compile time.

Practice

  1. Constrain a template to floating-point types with std::floating_point.
  2. Sort a vector with std::ranges::sort.
  3. Build a view that selects positive values and doubles them.
Show solutions
  1. Use template<std::floating_point T>.
  2. Call std::ranges::sort(values).
  3. Pipe through views::filter then views::transform.

Summary

Concepts describe template requirements; ranges provide range-oriented algorithms and lazy views. Both improve the expressiveness of modern generic C++.

References