Use of Concepts

Get an overview of the primary use cases of C++ concepts.

Concepts are one of the major new features added to C++20. Concepts are an extension for templates that allow direct expression of the programmer’s intent. According to C++ creator Bjarne Stroustrup, “concepts complete C++ templates as originally envisioned.”

Where can C++ concepts be used?

  • We can use concepts to perform compile-time validation of template arguments through boolean predicates. Checks are performed at the point of call, therefore the error messages are more understandable than they were previously when they would appear at the time of instantiation.

  • We can also use concepts to perform function dispatch based on the properties of types.

  • With concepts, we can require both syntactic and semantic conditions. In terms of syntactic requirements, imagine that we can impose the existence of certain functions in the API of any class. For example, we can create a concept Car that requires the existence of an accelerate function as shown in the code block below.

#include<concepts>
template<typename C>
concept Car=requires(C car){
car.accelerate();
};

In this course, we’ll mostly encounter examples with only a few requirements. While we still lack the best practices on how to write good concepts because it’s a new feature, there is already an agreement in the C++ community that good concepts do not specify a minimum number of requirements; they must satisfy all. This is to ensure that we do not create concepts that are satisfied accidentally.

If we write concepts that completely model an idea in a semantically coherent way, we’ll end up with more widely usable concepts. If we don’t reach that point on our first try, that’s perfectly okay. We’ll get there eventually!

With that said, in this course, we’ll mostly find incomplete concepts so we can focus on their particular parts.

Syntactic requirements

Anything that we put in the requires clause describes syntactic requirements of the types used to model a concept.

We’ll discuss the syntax in detail later on in the course.

Semantic requirements

Concepts not only express syntactic requirements but also semantic ones. At the same time, it is true that C++ concepts cannot express semantic requirements directly, at least not all of them.

Some semantic requirements are related to mathematical axioms, such as associativity or commutativity.

a+b==b+a//commutativity
(a+b)+c==a+(b+c)//associativity

These can be expressed with code. In fact, we partly did just that.

Others, like the time or space complexity of an operation, cannot be expressed in code. We have to rely on code comments or library documentation for them.

If we’re looking for examples, it’s worth looking into the standard library. Take, for example, std::equality_comparable.

Note: You can read more about the std::equality_comparable concept in the official C++ documentation.

It requires that:

  • The two equality comparison operators between the passed types are commutative.
  • == is symmetric, transitive and reflexive.
  • equality_comparable_with<T, U> is modeled only if, given any value t of type const std::remove_reference_t<T> and any value u of type const std::remove_reference_t <U>, and if C is std::common_reference_t<const std::remove_reference_t<T>&, const std::remove_reference_t<U>&>*, then bool(t == u) == bool(C(t) == C(u)).