Blog 4 min read

C++20 Concepts: Eliminating Generics Paradigm Drawbacks

Share this article
C++20 Concepts: Eliminating Generics Paradigm Drawbacks

As Bjarne Stroustrup points out, “C++ is a multi-paradigmed language.” It supports many different styles of programs, or paradigms, and object-oriented programming is only one of these. Some of the others are structured programming and generic programming. In the last few years, C++ experts like Andrei Alexandrescu, Scott Meyers and Herb Sutter have promoted the use of generic programming, and they call it Modern C++ Design.

Here’s what Andrei Alexandrescu says about Modern C++ Design:

Modern C++ Design defines and systematically uses generic components - highly flexible design artifacts that are mixable and matchable to obtain rich behaviors with a small, orthogonal body of code.

Three assertions in his point of view are interesting:

  • Modern C++ Design defines and systematically uses generic components.
  • Highly flexible design.
  • Obtain rich behaviors with a small, orthogonal body of code.

On the other side, OOP is very popular: inheritance and RTTI are two powerful mechanisms to design a C++ application, and many developers prefer this paradigm over the generic programming approach.

Here's the common definition of the inheritance:

In object-oriented programming (OOP), inheritance is when an object or class is based on another object (prototypal inheritance) or class (class-based inheritance), using the same implementation (inheriting from an object or class) specifying implementation to maintain the same behavior (realizing an interface; inheriting behavior). It is a mechanism for code reuse and to allow independent extensions of the original software via public classes and interfaces.

Many C++ experts recommend not overusing inheritance and the dynamic polymorphism mechanism. After all, what’s wrong with inheritance?

Short answerVery high coupling.

Let’s take as an example the implementation of a class calculating a tax.

generics1

The CTaxCalculator collaborates only with classes inheriting from ICalculator and can’t use any other non-ICalculator class, even if it could help calculate the tax. The calculator implementation class will be highly coupled with ICalculator, with no possibility to use another kind of class unless we introduce many other classes and interfaces to bypass this limitation. For example, the adapter pattern is a solution to bypass the inheritance high-coupling issue. Think about it: some GOF design patterns exist to resolve issues generated by the inheritance high coupling.

Generic programming to the rescue

With generic programming, the same tax calculator could be implemented like this:

generics2

The CGenericTaxCalculator class, on the other side, calculates the tax by using any type capable of calculating the tax, and it’s not aware of its kind. What’s important is the methods implemented by the type and not the class kind. That’s what makes generic programming more natural and flexible. Indeed, in the first implementation it’s like in the real world: a company looking for a developer accepts only graduates from a specific school and rejects all the others even if they have the needed skills. But flexibility comes with a price: the code becomes hard to understand. Indeed, in OOP programming I can just go to the definition of ICalculator to know what we expect from this type. However, with the generic programming approach, it’s difficult to know what we expect exactly from the template parameter: which members must it contain? Which constraints must be satisfied?

C++20 concepts to the rescue

Here's a short description of the C++20 concepts:

Concepts are an extension to C++'s templates, published as an ISO Technical Specification ISO/IEC TS 19217:2015.[1] They are named boolean predicates on template parameters, evaluated at compile time. A concept may be associated with a template (class template, function template, or member function of a class template), in which case it serves as a constraint: it limits the set of arguments that are accepted as template parameters.

The concepts feature was postponed many times. The good news is that C++20 will include this interesting feature.

We can discover in this interesting document the motivation behind the concepts feature:

The intent of concepts is to model semantic categories (Number, Range, RegularFunction) rather than syntactic restrictions (HasPlus, Array). According to ISO C++ core guideline T.20, "The ability to specify a meaningful semantics is a defining characteristic of a true concept, as opposed to a syntactic constraint."

With concepts we can resolve the constraint specification issue to make the code more readable and maintainable. It’s true that Boost has provided a concepts implementation for many years, but adding them to the language will make C++ more powerful and unique.

Another well-known drawback of generic programming is its error messages. Sometimes we can’t easily understand why an error is reported by the compiler, and we can waste our time figuring out exactly why it’s not working as expected.

Fortunately, the concepts feature will also improve the error messages, as explained here.

Summary

If inheritance is overused when choosing the OOP approach, it introduces high coupling between classes and forces you to add more classes just to resolve this issue — which is not the case when you choose to adopt the generic programming approach. And fortunately, the missing piece to implement readable, maintainable, low-coupled code will be part of the language soon.

Share this article