What is Concept?

A named set of compile-time requirements or constraints that a template type argument must satisfy. Essentially, it acts as a "gatekeeper" for templates, ensuring that the data types passed into a template are capable of performing the operations required inside that template
Concepts is a Predicate means evaluated at compile time
Problem Before Concepts:
  Before C++20, if you passed an incompatible data type (like a std::string) into a template function that performs mathematical multiplication, the program would still try to compile it. The compiler would fail deep within the function template, generating thousands of lines of unreadable error messages.
How Concepts Fix This
  With concepts, the compiler checks your data type before trying to instantiate the template. If the type doesn't fit the requirements, the compilation stops immediately and gives a clear, highly readable error message

#include <iostream>
#include <concepts>

// 1. Define the concept
template <typename T>
concept Multipliable = requires(T a) {
    a * a; // The type T must support the '*' operator
};

// 2. Apply the concept to a template function
template <Multipliable T>
T doubleValue(T value) {
    return value * 2;
}

int main() {
    // This works perfectly because integers support multiplication
    cout << doubleValue(5);    // Outputs: 10

    // UNCOMMENTING THE LINE BELOW WILL CAUSE A CLEAN COMPILE ERROR:
    // cout << doubleValue("hello"); 
    // Error: constraints not satisfied because 'const char*' is not Multipliable
}

Contraint

This is a compile-time condition that limits the types that can be passed into a template. C++20 gives you three main ways to apply constraints to your code:

1. The requires Clause (Trailing or Ad-hoc)

You can use the requires keyword to specify an explicit constraint after your template declaration.

#include <concepts>

// The constraint here is: std::integral <T>
template <typename T>
requires integral <T>
T modulo(T a, T b) {
    return a % b;
}

2. Constrained Template Parameters

Instead of using typename, you can use a concept name directly as the type parameter. This is a shorter, more readable constraint syntax.

#include <concepts>

// 'std::integral' acts as the constraint on type T
template <integral T>
T modulo(T a, T b) {
    return a % b;
}