0
0
CppHow-ToIntermediate · 3 min read

How to Use Concepts in C++: Syntax and Examples

In C++, concepts let you specify rules for template parameters to make code clearer and safer. You define a concept as a compile-time predicate and use it to constrain templates with requires clauses or directly in template parameter lists.
📐

Syntax

A concept is defined using the concept keyword followed by a boolean expression that checks properties of types. You can then use it to constrain templates by adding requires clauses or by specifying the concept in the template parameter list.

  • Define concept: template<typename T> concept ConceptName = /* condition */;
  • Use in template: template<ConceptName T> void func(T t); or template<typename T> requires ConceptName<T> void func(T t);
cpp
template<typename T>
concept Incrementable = requires(T x) {
    { ++x } -> std::same_as<T&>;
    { x++ } -> std::same_as<T>;
};

// Using concept in template parameter
template<Incrementable T>
void increment(T& value) {
    ++value;
}

// Using requires clause
template<typename T>
requires Incrementable<T>
void increment2(T& value) {
    ++value;
}
💻

Example

This example defines a concept Incrementable that checks if a type supports both prefix and postfix increment operators. It then uses this concept to constrain a template function that increments a value.

cpp
#include <iostream>
#include <concepts>
#include <string>

template<typename T>
concept Incrementable = requires(T x) {
    { ++x } -> std::same_as<T&>;
    { x++ } -> std::same_as<T>;
};

template<Incrementable T>
void increment(T& value) {
    ++value;
}

int main() {
    int a = 5;
    increment(a);
    std::cout << a << "\n"; // Output: 6

    // Uncommenting below will cause compile error because std::string is not Incrementable
    // std::string s = "hello";
    // increment(s);

    return 0;
}
Output
6
⚠️

Common Pitfalls

Common mistakes when using concepts include:

  • Not including <concepts> header which defines standard concepts like std::same_as.
  • Writing concepts that are too strict or too loose, causing unexpected compile errors or allowing wrong types.
  • Forgetting to use requires or concept constraints in templates, losing the benefits of concepts.
  • Using concepts with older compilers that do not support C++20 features.
cpp
/* Wrong: Missing requires clause or concept constraint */
template<typename T>
void increment_wrong(T& value) {
    ++value; // May compile but no concept check
}

/* Correct: Using concept to constrain template */
template<Incrementable T>
void increment_right(T& value) {
    ++value;
}
📊

Quick Reference

Concept UsageDescription
Define concepttemplate concept Name = /* boolean condition */;
Use in template parametertemplate void func(T t);
Use requires clausetemplate requires Name void func(T t);
Check expressionrequires(T x) { /* expressions that must be valid */ };
Standard conceptsUse header for std::same_as, std::integral, etc.

Key Takeaways

Concepts let you specify clear rules for template parameters to catch errors early.
Define concepts with boolean expressions using the requires keyword and use them to constrain templates.
Use header for standard concepts like std::same_as.
Concepts improve code readability and compiler error messages.
Ensure your compiler supports C++20 to use concepts.