C++20coreadvanced
Constraints and Requires Clauses (C++20 Addition)
C++20 · The Big Four · Compiler flag: -std=c++20
Explanation
Specify requirements for template parameters using `requires` clauses.
Real-World Scenario
Ensure a template function accepts only floating-point types in a physics engine.
Quick Snippet
snippet.cpp
template<typename T> requires std::floating_point<T> void compute(T t) {}Full Example
constraints_requires.cpp
// CodeNebula - C++20 Example: Constraints and Requires
// Compile: g++ -std=c++20 -Wall -Wextra -o example constraints_requires.cpp
// Demonstrates requires clauses and requires expressions: simple requirements,
// type requirements, compound requirements, and nested requirements.
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
#include <concepts>
// ------------------------------------------------------------------
// 1. Simple requires clause on a function template
// ------------------------------------------------------------------
template <typename T>
requires std::integral<T>
T gcd(T a, T b) {
while (b != 0) {
T temp = b;
b = a % b;
a = temp;
}
return a;
}
// ------------------------------------------------------------------
// 2. Requires expression - ad-hoc constraint
// ------------------------------------------------------------------
template <typename T>
requires requires(T a, T b) {
{ a + b } -> std::convertible_to<T>;
{ a * b } -> std::convertible_to<T>;
}
T multiply_and_add(T a, T b, T c) {
return a * b + c;
}
// ------------------------------------------------------------------
// 3. Requires expression with type requirements
// ------------------------------------------------------------------
template <typename Container>
requires requires(Container c) {
typename Container::value_type;
typename Container::iterator;
{ c.size() } -> std::convertible_to<std::size_t>;
{ c.begin() } -> std::same_as<typename Container::iterator>;
}
void print_container(const Container& c) {
std::cout << " [";
bool first = true;
for (const auto& elem : c) {
if (!first) std::cout << ", ";
std::cout << elem;
first = false;
}
std::cout << "] (size=" << c.size() << ")" << std::endl;
}
// ------------------------------------------------------------------
// 4. Nested requires - constraint within a constraint
// ------------------------------------------------------------------
template <typename T>
requires requires(T val) {
requires std::is_arithmetic_v<T>;
requires sizeof(T) >= 4;
}
T safe_divide(T a, T b) {
if (b == T{}) return T{};
return a / b;
}
// ------------------------------------------------------------------
// 5. Trailing requires clause on member function
// ------------------------------------------------------------------
template <typename T>
struct Wrapper {
T value;
void print() const requires requires(std::ostream& os, T v) {
os << v;
} {
std::cout << " Wrapper(" << value << ")" << std::endl;
}
T doubled() const requires std::integral<T> || std::floating_point<T> {
return value + value;
}
};
int main()
{
std::cout << "=== Requires Clause (gcd) ===" << std::endl;
std::cout << " gcd(12, 8) = " << gcd(12, 8) << std::endl;
std::cout << " gcd(100, 75) = " << gcd(100, 75) << std::endl;
std::cout << "\n=== Requires Expression (multiply_and_add) ===" << std::endl;
std::cout << " 3*4+5 = " << multiply_and_add(3, 4, 5) << std::endl;
std::cout << " 1.5*2.0+0.5 = " << multiply_and_add(1.5, 2.0, 0.5) << std::endl;
std::cout << "\n=== Type Requirements (print_container) ===" << std::endl;
print_container(std::vector<int>{10, 20, 30});
print_container(std::vector<std::string>{"a", "b", "c"});
std::cout << "\n=== Nested Requires (safe_divide) ===" << std::endl;
std::cout << " 10 / 3 = " << safe_divide(10, 3) << std::endl;
std::cout << " 7.5 / 2.5 = " << safe_divide(7.5, 2.5) << std::endl;
std::cout << " 5 / 0 = " << safe_divide(5, 0) << std::endl;
std::cout << "\n=== Trailing Requires (Wrapper) ===" << std::endl;
Wrapper<int> wi{42};
wi.print();
std::cout << " Doubled: " << wi.doubled() << std::endl;
Wrapper<std::string> ws{"hello"};
ws.print();
return 0;
}
/*
Expected output:
=== Requires Clause (gcd) ===
gcd(12, 8) = 4
gcd(100, 75) = 25
=== Requires Expression (multiply_and_add) ===
3*4+5 = 17
1.5*2.0+0.5 = 3.5
=== Type Requirements (print_container) ===
[10, 20, 30] (size=3)
[a, b, c] (size=3)
=== Nested Requires (safe_divide) ===
10 / 3 = 3
7.5 / 2.5 = 3
5 / 0 = 0
=== Trailing Requires (Wrapper) ===
Wrapper(42)
Doubled: 84
Wrapper(hello)
*/
Run command
g++ -std=c++20 -Wall -Wextra -o example constraints_requires.cpp