5.2.18C++ Programming

Concepts (C++20) — constraining templates

2,052 words9 min readdifficulty · medium1 backlinks

WHY do concepts exist?


WHAT is a concept?


HOW do you write & apply them? (4 syntaxes, same meaning)


Building concepts from scratch: the requires expression

There are two different requires. Don't confuse them.

The four requirement kinds inside { }:

Kind Example Means
simple a + b; expression is valid
type typename T::value_type; nested type exists
compound { *p } -> std::convertible_to<int>; valid and type constraint
nested requires Numeric<T>; another constraint holds
Figure — Concepts (C++20) — constraining templates

Overloading on concepts (the killer feature)


Steel-manning common mistakes


Worked example: a generic print_all


Flashcards

What is a C++20 concept, in one phrase?
A named compile-time boolean predicate on template parameters (a constexpr bool template) used to constrain templates.
What is the difference between a requires-clause and a requires-expression?
A clause introduces a constraint and evaluates a bool (requires Numeric<T>); an expression builds a bool by listing expressions that must compile (requires(T a){ a+a; }).
What does { a + b } -> std::same_as<T> check?
A compound requirement: a+b must be a valid expression AND its type must be exactly T.
When two constrained overloads both apply, which is chosen?
The more-constrained one (subsumption) — strictly stronger requirements win.
What happens when a template's constraint is NOT satisfied?
The template is silently removed from the candidate set (not an error) unless no candidates remain.
Name the four requirement kinds inside a requires-expression.
simple, type (typename T::x), compound ({e}->C), nested (requires C<T>).
Does a requires-expression run code at runtime?
No — it only tests at compile time whether each listed expression is well-formed.
Give the shortest syntax to constrain a parameter x to Numeric.
auto f(Numeric auto x) — abbreviated function template.

Recall Feynman: explain to a 12-year-old

Imagine a toy machine that only works with batteries. Old C++ let you stuff anything in — a banana, a rock — and only exploded once it tried to use the battery deep inside, with a confusing mess. A concept is a sticker on the slot that says "batteries only." Now if you bring a banana, the machine politely says "nope, this slot needs a battery" right at the door. Same idea: a concept checks "can this type do what I need?" before anything breaks.

Connections

  • Templates — function & class templates
  • SFINAE and std::enable_if (the old, painful way concepts replace)
  • type_traits — std::integral, std::floating_point
  • Overload Resolution & Subsumption
  • constexpr — compile-time evaluation
  • Ranges library (C++20) (heavily built on concepts)
  • auto and decltype

Concept Map

cause

motivates

is a

used as

if unsatisfied

enables

written via

introduced by

built from

checks

moves errors to

Pre-C++20 duck-typed templates

Deep cryptic template errors

Concept: named compile-time predicate

constexpr bool on types

Constraint restricts template

Removed from candidate set

Overload on requirements

Four constraint syntaxes

requires-clause returns bool

requires-expression lists valid exprs

Every listed expression compiles

Clear call-site error

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, C++20 se pehle templates "duck typing" karte the — matlab tum kisi bhi type ko template mein daal sakte the, aur compiler tab tak khush rehta tha jab tak woh type kuch unsupported operation par na pohonche. Phir error template ke andar 200 lines deep phat-ta tha aur samajh hi nahi aata tha. Concept isi dard ka ilaaj hai: ek concept basically ek naam wala compile-time boolean predicate hai jo bolta hai "yeh type itne kaam kar sakta hai ya nahi?" — aur yeh check call site par hi hota hai, andar nahi. Isliye error clean aata hai: "string ne Numeric satisfy nahi kiya."

Do tarah ke requires hote hain, inhe confuse mat karna. Ek hai requires-clause jo constrain karta hai (template<Numeric T>), aur doosra hai requires-expression jo check karta hai ki kuch expressions compile hote hain ya nahi (requires(T a){ a + a; }). Yaad rakhne ke liye: "Clause Constrains, Expression Examines." Important baat — requires-expression koi value calculate nahi karta runtime par; woh sirf poochta hai "yeh line compile hoti hai kya?" aur true/false deta hai.

Sabse mast feature hai overloading on concepts. Tum integral ke liye ek function likh sakte ho aur floating_point ke liye doosra. describe(5) integral wala uthayega, describe(3.14) floating wala. Jab kisi overload ka constraint satisfy nahi hota, woh chup-chaap candidate list se hat jaata hai — error nahi aata, bas use nahi karta. Aur agar do-do constraints satisfy ho jaayein, toh jo zyada strict (more-constrained) hai woh jeet ta hai — isko subsumption bolte hain. Yeh purane enable_if/SFINAE ke jhanjhat ka clean replacement hai, isliye modern C++ ke liye must-know hai, especially Ranges library samajhne ke liye.

Go deeper — visual, from zero

Test yourself — C++ Programming

Connections