Some times it happens that we just want to define an interface, and some of those times we think that we should not use inheritance for some performance-related reasons. Most of these times we are very wrong, and we should just inherit from a base class with (possibly pure) virtual functions. However in this post I will explain an alternative way for the minority of the cases where we actually cannot use inheritance, but we still want to define an interface. We will do so by employing one of the latest features of the C++ standard: Concepts!

The argument against dynamic polymorphism revolves around the v-table (short for “virtual” table), which is how C++ actually implements virtual member functions. The fact is that virtual functions work seemingly by magic, but this magic turns out is just an extra pointer to a hidden struct containing function pointers; this struct is the “v-table” and every derived class has a different one. Hence the “problem”: an invocation of a virtual method is not just one jump to the code of the function, but 2 jumps, because first we need to dereference the pointer of the v-table! (a complete explanation of how the v-table works is beyond the scope of this post and there are a lot of nice explanations around)

The arguments in favour of dynamic polymorphism are many, first and foremost that it is the standard and most importantly simplest way to achieve the result. The emphasis on “standard” cannot be overstated. The best code is the code that is easy to read: people understand commonly used patterns and hate it when they need to read comments to make heads or tails of what’s going on (even worse when there is no comment at all!). A second argument, and one that is often overlooked, is that performance does not have to suffer necessarily: there are circumstances where we get dynamic polymorphism for free! This happens when the compiler manages to “devirtualize” the calls, and more in general when the branch predictor of our CPU is able to correctly guess which function implementation we are going to use, therefore preloading the right instructions and hence avoiding the cost of the extra jump.

That being said, IF you need extreme control over the code and IF you are trying to squeeze every single inch of performance out of your application (2 big IFs), then you probably already know the current common way of declaring interfaces without virtual functions: CRTP. The “Curiously recurring template pattern” is a clever way to achieve what we want to achieve in this post, but it’s not the only way, and perhaps not the nicest way either. In this post we are going to do the same that can be done with CRTP, but we’ll do so (ab)using C++20 Concepts. Here is how.

Let’s first define our “interface”, which will be (you guessed it) a Concept:

template <typename T> concept Shape = requires(const T& t) { { t.area() } ->float; };

The above declares that our type should have one const function called “area” that returns a float. This is usually employed as a constraint for template parameter in functions or classes, e.g.

template <Shape S> void foo(S){}

Which does not compile if the object passed to foo() does not satisfy the Shape concept. For instance if we declare a Rectangle struct that almost satisfies the constraint:

struct Rectangle { float area(); // Forgot the const! }; int main() { Rectangle r; foo(r); }

we get:

.../src/main.cpp:48:10: error: cannot call function ‘void foo(S) [with S = Rectangle]’ 48 | foo(r); | ^ .../src/main.cpp:10:6: note: constraints not satisfied

The question is: is there a way to declaratively state that Rectangle is a Shape, perhaps directly in its header? Can we do so by employing the same concept “technology” that we used for foo()? Yes and no (and yes).

What I personally would like to be able to do is something like the following:

struct Rectangle requires Shape { float area() const; };

This way the first line of the declaration of the Rectangle type clearly states that this class satisfies (in a way “inherits”) from Shape. The syntax gods however did not allowed us to be this straightforward, as the raison d’etre of the concept feature is not to declare types but to help constraint which types should be allowed in template substitutions.

Enter static_assert:

struct Rectangle { float area() const; }; static_assert(Shape<Rectangle>);

This does exactly what we’d expect: the clever designers of the concepts feature allowed us to use static_asserts to achieve our goal, even if we need to get to the bottom of the declaration to see it action.

But what if Rectangle was a class template? Then we could not write:

template<typename T> struct Rectangle { float area() const; T base; T height; }; static_assert(Shape<Rectangle<...>>); // We need to provide a template parameter here!

Or better, we could write it like that, but we would also need to come up with a valid template parameter, which in general could be any complex type that might or might not be known by the developer at the time of writing the class (the header might be in a library and it might be a completely unknown user-type). So, instead of coming up with some random template parameter we could use the following trick:

template <typename T> struct Rectangle { Rectangle() { static_assert(Shape<decltype(*this)>); } float area() const; T base; T height; };

The static_assert is moved to the constructor, then we can just use decltype to obtain the instantiated type and do the check! It goes without saying that this also works in the previous case where we didn’t have a template class 😉

So there you have it, for the small small price of declaring a constructor (which most of the times you have anyway), you can make sure that your class satisfies any concept (i.e. “interface”) of your choosing, and all without employing CRTP.

We could take it one step further, and make it look more like inheritance by moving closer to CRTP territory. It’s enough to declare a base class and move the constraint to the base class constructor, then we can inherit from that like we would for normal CRTP:

template <typename T> struct ShapeBase { ShapeBase() { static_assert(Shape<T>); } }; template <typename T> struct Circle : ShapeBase<Circle<T>> { float area() const; T radius; };

Is it better than just adding the static_assert directly in the “Derived” constructor? You choose 😛

All the code is in the repo. For the lazy here is the link with the working example in godbolt.

As usual, suggestions/comments/corrections are welcome 🙂

Cheers!

EDIT:

Following the discussion on reddit, here is the modified (and better!) version without the decltype

template <typename T> struct Square { Square() { static_assert(Shape<Square<T>>); } float area() const; T edge; };

thanks to u/eacousineau for giving me the idea.

EDIT2:

As pointed out by /u/anonymous2729 et al. we could improve still by making the base class trivially default constructible, which we lost once we added a user-defined constructor. To be more specific, a class is trivially default constructible if the following conditions hold:

It is implicitly declared or explicitly defaulted .

. A has no virtual functions and no virtual base classes

All the direct base classes of A have trivial constructors

The classes of all the non-static data members of A have trivial constructors

So we can write:

template<class T> struct ShapeBase { ShapeBase() requires(Shape<T>) = default; }; template<class T> struct Circle : ShapeBase<Circle<T>> { float area() const; int radius; };

And the same can be done for the version that does not employ a base class. Here:

template<class T> struct Circle{ Circle() requires(Shape<Circle<T>>) = default; float area() const; int radius; };

Thank you all for the ideas!