Skip to content

Ch16: Concepts

TL;DR: Template Constraints: Concepts (C++20) constrain template parameters in function signatures, replacing type traits and static_assert with readable compile-time checks.

โšก Quick Reference

#include <iostream>
#include <concepts>
#include <type_traits>

// Custom concept declaration using standard traits
template <typename T>
concept MyConcept = std::is_integral_v<T>;

// Custom concept utilizing a requires expression
template <typename T>
concept Addable = requires(T a, T b) {
    a + b; // Simple requirement: checks syntactic validity
};

// Nested and compound requirement concepts
template <typename T>
concept TinyAndConvertible = requires(T a) {
    requires sizeof(T) <= 4; // Nested requirement: evaluates boolean condition to true
    { a + 1 } -> std::convertible_to<int>; // Compound requirement: checks syntax & convertible return type
};

// Syntax 1: requires clause after template declaration
template <typename T>
requires std::integral<T>
T add1(T a, T b) { return a + b; }

// Syntax 2: Replace typename directly in template parameter list
template <std::integral T>
T add2(T a, T b) { return a + b; }

// Syntax 3: Constrained auto parameter
auto add3(std::integral auto a, std::integral auto b) { return a + b; }

// Syntax 4: Trailing requires clause
template <typename T>
T add4(T a, T b) requires std::integral<T> { return a + b; }

// Combining concepts using logic OR / AND
template <typename T>
requires std::integral<T> || std::floating_point<T>
void print_num(T val) {
    // Constrains auto variable declarations
    std::integral auto copy = val;
}

int main() {
    return 0;
}

๐Ÿง  Core Concepts

  • Template Constraints: Concepts (C++20) constrain template parameters in function signatures, replacing type traits and static_assert with readable compile-time checks.
  • Requirements Clauses: Custom concepts enforce criteria using simple (syntactic check), nested (boolean check), and compound (syntactic and return-type checks) requirements inside requires clauses.
  • Flexible Syntaxes: Concepts can constrain parameters via parameter-list replacements (template<concept T>), trailing requires clauses, or prefixed auto variables.

โš ๏ธ Pitfalls (Quick Scan)

Mistake Fix
Using a simple requirement (e.g., sizeof(T) <= 4) for boolean assertions Wrap boolean expression checks inside a nested requires statement
Forgetting that operations (like char addition) trigger implicit promotion to int Use explicit return values or cast expression outcomes to match expected template constraints
Assuming syntactic operations in concepts (like a b) check logic/semantics Combine syntactic checks with type trait validation to verify semantic meaning
Declaring complex concepts inline inside requires clauses Define named concepts separately and reference them by name in template signatures
Relying solely on IDE squiggly lines for debugging concept errors Compile code with GCC to receive descriptive outputs specifying exactly which constraints failed

๐Ÿ“– Full Details

Cause โ†’ Effect โ†’ Fix with timestamp (click to expand) * **Using a simple requirement (e.g., `sizeof(T) <= 4`) for boolean assertions** -> **Compiler only checks if the expression is syntactically valid C++, ignoring the boolean result and incorrectly accepting large types** -> **Wrap boolean expression checks inside a nested `requires` statement (13:58)** * **Forgetting that operations (like `char` addition) trigger implicit promotion to `int`** -> **Concept-constrained return types fail constraints if the promoted type violates size limitations** -> **Use explicit return values or cast expression outcomes to match expected template constraints (18:50)** * **Assuming syntactic operations in concepts (like `a * b`) check logic/semantics** -> **Operations are accepted if they compile, regardless of whether the semantics make mathematical sense** -> **Combine syntactic checks with type trait validation to verify semantic meaning (09:35)** * **Declaring complex concepts inline inside `requires` clauses** -> **Function signatures become cluttered, ugly, and extremely difficult to read** -> **Define named concepts separately and reference them by name in template signatures (26:10)** * **Relying solely on IDE squiggly lines for debugging concept errors** -> **Diagnostic messages are often generic and fail to isolate the violated sub-constraint** -> **Compile code with GCC to receive descriptive outputs specifying exactly which constraints failed (05:30)**

๐Ÿ“Ž Repo Files

  • 25.02UsingConcepts/main.cpp
  • 25.03BuildingYourOwnConcepts/main.cpp
  • 25.04ZoomingInOnRequiresClause/main.cpp
  • 25.05CombiningConcepts/main.cpp
  • 25.06ConceptsAndAuto/main.cpp