Back to TILs

C++ Concepts

Date: 2022-09-20Last modified: 2023-03-06

Table of contents

An alternative to static asserts and type traits

Syntax 1

template <typename T>
  requires std::integral<T> // <---
T addA( T a, T b )
{
  return a + b;
}

template <typename T>
  requires std::is_integral_v<T> // Using a type trait
T addB( T a, T b )
{
  return a + b;
}

Syntax 2

template <std::integral T>
T addC( T a, T b )
{
  return a + b;
}

Syntax 3

auto addD( std::integral auto a, std::integral auto b )
{
  return a + b;
}

Syntax 4

template <typename T>
T addE( T a, T b )
  requires std::integral<T>
{
  return a + b;
}

Building your own concept

template <typename T>
concept MyIntegral = std::is_integral_v<T>;
template <typename T>
concept Multipliable = requires( T a, T b ) {
  a *b; // Just makes sure the syntax is valid
};
template <typename T>
concept Incrementable = requires( T a ) {
  a += 1;
  ++a;
  a++;
};
template <typename T>
concept Addable = requires( T a, T b ) {
  // noexcept is optional
  {
    a + b
  } noexcept -> std::convertible_to<int>; // Compound requirement
  // Checks if a + b is valid syntax, does not throw exceptions(optional),
  // and the result is convertible to int(optional)
};

Nested requirement

template <typename T>
concept TinyType = requires( T t ) {
  sizeof( T ) <= 4; // Simple requirement : Only checks syntax

  // Nested requirement: checks the if the expression is true
  requires sizeof( T ) <= 4;
};

Combining requirement

Conjunction &&

template <typename T>
  requires std::integral<T> && TinyType<T>
T addF( T a, T b )
{
  return a + b;
}

Disjunction ||

template <typename T>
  requires std::integral<T> || std::floating_point<T>
T addG( T a, T b )
{
  return a + b;
}

Concepts and auto

template <typename T>
void print_number( T n )
{
  static_assert( std::is_integral<T>::value, "Must pass in an integral argument" );
  std::cout << "n: " << n << std::endl;
}
class A {};

enum E : int {};

template <class T>
T f( T i )
{
  static_assert( std::is_integral<T>::value, "Integral required." );
  return i;
}
#define SHOW( ... ) std::cout << std::setw( 29 ) << #__VA_ARGS__ << " == " << __VA_ARGS__ << '\n'

int main( [[maybe_unused]] int argc, [[maybe_unused]] char **argv )
{

```cpp
  std::cout << std::boolalpha;

  SHOW( std::is_integral<A>::value );
  SHOW( std::is_integral_v<E> );
  SHOW( std::is_integral_v<float> );
  SHOW( std::is_integral_v<int> );
  SHOW( std::is_integral_v<const int> );
  SHOW( std::is_integral_v<bool> );
  SHOW( f( 123 ) );

  // clang-format off
  //    std::is_integral<A>::value == false
  //         std::is_integral_v<E> == false
  //     std::is_integral_v<float> == false
  //       std::is_integral_v<int> == true
  // std::is_integral_v<const int> == true
  //      std::is_integral_v<bool> == true
  //                      f( 123 ) == 123
  // clang-format on



## Possible output


```txt
   std::is_integral<A>::value == false
        std::is_integral_v<E> == false
    std::is_integral_v<float> == false
      std::is_integral_v<int> == true
std::is_integral_v<const int> == true
     std::is_integral_v<bool> == true
                     f( 123 ) == 123

References