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 numbersTemplate 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 typesWhy 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 doit.
#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;
}