Step 4 of 7
Concepts
A plain template accepts any type. If the type doesn't fit, the error appears deep inside the template's body, often as a wall of messages about code you didn't write. Concepts (C++20) state the requirements up front, in the signature, so misuse is rejected immediately with a short, clear error.
#include <concepts>
#include <iostream>
#include <string>
#include <vector>
template <std::integral T>
bool is_even(T n) { return n % 2 == 0; }
template <typename T>
concept Sized = requires(const T& t) {
{ t.size() } -> std::convertible_to<std::size_t>;
};
template <Sized C>
bool is_empty(const C& c) { return c.size() == 0; }
int main() {
std::cout << is_even(10) << is_even(7L) << "\n";
std::cout << is_empty(std::string("")) << is_empty(std::vector<int>{1}) << "\n";
// is_even(2.5); // error: 'double' does not satisfy 'integral'
// is_empty(42); // error: 'int' does not satisfy 'Sized'
}
10
10
How it works
template <std::integral T>replacestemplate <typename T>and says "T must be an integer type".std::integral,std::floating_point,std::same_asand many more live in<concepts>.- You can define your own concept with
requires: list expressions that must compile for the type.Sizedrequires asize()member returning something convertible to a size. - If a call doesn't satisfy the concept, the compiler says exactly that, and points at your call.
Your task: Euclid's algorithm
The greatest common divisor of a and b equals the gcd of b and a % b, and gcd(a, 0) is a. So loop while b != 0:
(48, 18) -> (18, 12) -> (12, 6) -> (6, 0) answer 6
Use a temporary to do the swap: T t = a % b; a = b; b = t;. The std::integral constraint is required: % doesn't work on doubles, and the tests check that a double call is rejected.
Your turn: write template <std::integral T> T gcd(T a, T b) using Euclid's algorithm: while b != 0, replace (a, b) with (b, a % b). The tests also check that calling it with a double is rejected.