Inheritance constraint The following demonstrates using a concept as an upper bound for inheritance constraints on types, by creating a concept ExtendsPlayer satisfied only by classes which inherit from a base class Player, blocking any type that does not. import std; using std::is_base_of_v; using std::vector; class Player { // ... }; template concept ExtendsPlayer = is_base_of_v; // T is required to be a type whose inheritance upper bound is Player, // blocking any type that does not inherit from Player template void processListOfPlayers(const vector& players) { // ... } This is similar to
constrained generics in
Java, and is equivalent to the following example: import java.util.List; class Player { // ... } public class Example { // T is constrained to types that inherit from Player public static void processListOfPlayers(List players) { // ... } }
The following is a possible definition of the concept std::equality_comparable from the header of the
C++ Standard Library. This concept is satisfied by any type T such that for
lvalues a and b of type T, the expressions a == b and a != b as well as the reverse b == a and b != a compile, and their results are convertible to a type that satisfies the concept "boolean-testable": /** * @namespace std * @brief The C++ Standard Library namespace */ namespace std { /** * @internal * @concept _WeaklyEqualityComparableWith * @brief The following concept is an implementation detail used to build equality_comparable * @tparam T the first template parameter to compare * @tparam U the second template parameter to compare */ template concept _WeaklyEqualityComparableWith = requires (const remove_reference& a, const remove_reference& b) { { a == b } -> same_as; { a != b } -> same_as; { b == a } -> same_as; { b != a } -> same_as; }; /** * @concept equality_comparable * @brief Checks that a == b, a != b, b == a, b != a compile and convert to bool * @tparam T the type to compare */ template concept equality_comparable = _WeaklyEqualityComparableWith; } A function template constrained on this concept may be declared as follows: // constrained abbreviated function template declaration // using a constrained placeholder type (Concept4 from above) void f(const equality_comparable auto& x); or // constrained function template declaration // using a type constraint (Concept1 from above) template void f(const T& x); And may be called as usual: // OK, int satisfies equality_comparable f(42);
Interfaces In
object-oriented programming, an
interface defines a set of method signatures which the class must implement in order to satisfy it. A concept may be seen as similar to an interface, which can define that a type satisfies various requirements. import std; using std::same_as; using std::floating_point; template concept Drawable = requires (T t) { { t.draw() } -> same_as; // T::draw must return void { t.area() } -> floating_point; // matches any floating-point type }; // The class Circle satisfies the Drawable concept // Circle does not need to declare it satisfies Drawable, however class Circle { private: const double radius; public: explicit Circle(double r): radius{r} {} void draw() const { // draw a circle... } double area() const noexcept { return std::numbers::pi * radius * radius; } }; // Only types which satisfy Drawable may be used template void render(const T& shape) { // ... shape.draw(); std::println("Drew shape with area {}", shape.area()); } This would be roughly similar to the following in Java: interface Drawable { void draw(); // must return void double area(); // must return double } // Circle must explicitly declare that it implements Drawable class Circle implements Drawable { private final double radius; public Circle(double r) { this.radius = r; } @Override public void draw() { // draw a circle... } @Override public double area() { return Math.PI * radius * radius; } } // Only types which implement Drawable may be used static void render(Drawable shape) { // ... shape.draw(); System.out.printf("Drew shape with area %f", shape.area()); } C++ concepts resemble
Go interfaces very closely. Unlike Java interfaces, Go interfaces do not require a type to declare that it implements the interface, as types implicitly satisfy the interface as long as it implements all of its requirements. For example, this Drawable concept in C++ is roughly equivalent to the Go interface: import "math" type Drawable interface { Draw() Area() float64 } type Circle struct { radius float64 } func (c Circle) Draw() { // draw a circle... } func (c Circle) Area() float64 { return math.Pi * c.radius * c.radius } == Compiler diagnostics ==