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 promotes the uses of the generic programming and they qualify it as Modern C++ Design.
Here’s what say Andrei Alexandrescu about the 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 are interesting in his point of view:
- 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 the OOP is very popular, the inheritance and the RTTI are two powerful mechanisms to design the C++ application and many developers prefer this paradigm in favor of 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 to not overuse the inheritance and the dynamic polymorphism mechanism. After all what’s wrong with the inheritance?
Short answer: Very high coupling.
Let’s take as example the implementation of a class calculating a tax.
The CTaxCalculator collaborates only with the classes inheriting from the ICalculator and can’t use any other not ICalculator class even if it can help to calculate the Tax. The calculator implementation class will be highly coupled with ICalculator and no possibility to use another class kind 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 are there to resolve some issues generated by the inheritance high coupling issue.
Generic programming to the rescue
Using the generic programming, the same Tax calculator could be implemented like this:
The CGenericTaxCalculator class in the other side calculate the tax by using any type capable to calculate the tax and it’s not aware about its kind. What’s important is the methods implemented by the type and not the class kind. What makes the generic programming more natural and flexible. Indeed in the first implementation it’s like in the real world, a company searching for a developer accept only ones graduated from a specific school and reject all the others even if they have the skills needed.
But the flexibility comes with a price, the code become hard to understand. Indeed in OOP programming I can just go to the definition of ICalculator to know what we wait for this type. However for the generic programming approach, it’s difficult to know what we expect exactly from the template parameter? Which members must contains? 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. 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 concept feature was postponed many times. The good news is that the C++20 will includes this interesting feature.
We can discover this interesting document the motivation behind the concept 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 constraints specifications issue to make the code more readable and maintainable. It’s true that Boost provides for many years a concept implementation but adding them to the language will make C++ more powerful and unique.
Another most known drawback of generics programming is their error messages. Sometimes we can’t easily understand why an error is reported by the compiled and we can waste our time to know exactly why it’s not working as expected.
Fortunately the concepts feature will also improve the error message as explained here.
If the inheritance is overused when choosing the OOP approach, it’s introduce a high coupling between classes and force you to add more classes just to resolve this issue, it’s not the case when choosing to adopt the generic programming approach. And fortunately the missing piece to implement a readable, maintainable, low coupled code will be part of the language soon.