Concepts: Overview

Motivation

  • Error messages on failed template instantiations are brutal

  • ⟶ Solution needed

  • Template metaprogramming is not a solution

    • Few people are able to understand it

    • … let alone read it

  • Enter Concepts: compiletime template parameter validation

  • Relatively easy language to formulate what’s needed for successful substitution

  • Let’s walk through a simple example: using the std::integral concept from <concepts>

No Problem: Template Instantiation Chosen By Parameter Type

  • Everything fine

  • Template used with compatible types (compatible?)

  • ⟶ no errors

template <typename T>
bool is_even(T num)
{
    return (num % 2) == 0;
}
   
int main()
{
    is_even(3);
    is_even(4UL);
    is_even(4L);
    return 0;
}

Problem: Template Instantiation Chosen By Parameter Type

  • Not fine

  • % not defined for floating point numbers

  • Template instantiated nonetheless

  • ⟶ Error (possibly deep down complicated code)

  • ⟶ User can only guess

#include <iostream>

template <typename T>
bool is_even(T num)
{
    return (num % 2) == 0;                             // <-- % on T
}

int main()
{
    is_even(1.5);                                      // <-- requiring % on T=double
    return 0;
}
In instantiation of ‘bool is_even(T) [with T = double]’:
   required from here
error: invalid operands of types ‘double’ and ‘int’ to binary ‘operator%’
    6 |     return (num % 2) == 0;
      |            ~~~~~^~~~

Concepts To The Rescue

  • Implicit constraint: % is only defined for integral types

  • Why not make that constraint explicit?

  • A case for <concepts>

  • std::integral

#include <concepts>
#include <iostream>

template <typename T>
requires std::integral<T>                              // <-- "cacnonical" syntax
bool is_even(T num)
{
    return (num % 2) == 0;
}

int main()
{
    is_even(1.5);
    return 0;
}
note: constraints not satisfied
   required from here                                  # <-- watch out for this
/usr/include/c++/13/concepts:100:13:   required for the satisfaction of ‘integral<T>’ [with T = double]
/usr/include/c++/13/concepts:100:24: note: the expression ‘is_integral_v<_Tp> [with _Tp = double]’ evaluated to ‘false’
  100 |     concept integral = is_integral_v<_Tp>;
      |                        ^~~~~~~~~~~~~~~~~~

Alternative Concept Syntaxes

C++ does not follow rule 13 of “The Zen of Python”:

There should be one– and preferably only one –obvious way to do
it.
#include <concepts>
#include <iostream>

template <typename T>
bool is_even(T num) requires std::integral<T>          // <--- same, but different
{
    return (num % 2) == 0;
}

int main()
{
    is_even(42);
    return 0;
}
#include <concepts>
#include <iostream>


template <std::integral T>                             // <--- cool (typename is soo boring)
bool is_even(T num)
{
    return (num % 2) == 0;
}

int main()
{
    is_even(42);
    return 0;
}
#include <concepts>
#include <iostream>

bool is_even(std::integral auto num)                   // <--- cool: abbreviated function templates
{                                                      //      (with constraint std::integral)
    return (num % 2) == 0;
}

int main()
{
    is_even(42);
    return 0;
}