Both C++ templates and C# generics serve a similar purpose. But where C# uses constraints to enable generics, C++ instead uses the compiler to enable templates. And C++ includes the ability to create templates base on values which will enable you to do things unheard of in C#. Most C# developers don’t even know what they’re missing.
When you declare a generic class in C#, you need to specify constraints so that the compiler can figure out if the types that you’re trying to use with the generic class actually meet the requirements or not. If you forget a constraint, then it’s possible to compile code that won’t run. And if you add too many constraints, then it’s possible to generate unnecessary compile errors.
C++ doesn’t use constraint. Instead it looks at how the types are actually used and makes sure that a type has all the methods needed by the template. So if you want to use a class as a template parameter, then just look at how the type T, or whatever it’s called in the template, is actually used and make sure that your class would be able to fit. That’s all there is to it. The compiler will make sure that your template parameters meet the needs of the template class.
You can also specify a value for your template parameters in C++. This allows you to create distinct types that are slightly different. Maybe you want a door that always opens in vs. another door that always opens out. Instead of creating two door classes, an inDoor and an outDoor, just create a single template door class that takes a bool value to select the direction. The compiler will keep your door class as its own type and make sure that it’s a different type than a door class.
Listen to the full episode or you can also read the full transcript below.
If you haven’t already, make sure to listen to the previous episode #57 first because this episode builds on that episode. Many of the core concepts of C# generics apply to C++ templates but with some difference that this episode will explain.
I mentioned in the intro that C++ relies more on the compiler than C#. How does this work? I actually find C++ to be a more natural and direct way to express a template.
First of all C++ does not have a concept of a base class that’s common to all other classes. Some languages have this idea but not C++. You can create a C++ class that is completely unrelated to any other class if you want.
Interfaces also play a huge role in C# generics. And while C++ does have pure virtual abstract classes that serve a similar purpose, you’re not limited to working with interfaces if you don’t want to.
One of the problems that C# encountered that I explained in the previous episode was knowing what methods and properties were safe to call from within a generic class method. C# uses constraints to make sure that you can’t create a generic class with a type that fails to meet the constraint.
C++ takes a different approach. It checks during compile time if you try to create an instance of a template class if the type you’re using will match the needs of the template. What do I mean by this?
Let’s take the adventure game again and start with the classes shop which has a trade method, a tradable class that defines a pure virtual method to get the original value, an inventoryItem class which inherits from tradable and implements the method to get the original value, and a service class which also inherits from tradable and implements the method to get the original value.
So far, the only thing that’s really changed is that the tradable interface has become a pure virtual abstract class.
You just write the shop class trade method to accept a type T just like before and go ahead and call the original value method on T when you need to.
When some code wants to create a shop of inventory items what happens is that the compiler makes sure right then that the template code will compile properly or not. It sees that your trade method is trying to call a method on the type T to get the original value and it looks at the inventory item being used to construct a shop and makes sure that method exists. If not, then you get a compile error.
Now, C# can also give you a compile error but for a different reason. The C# compiler just looks at the constraints to decide whether or not to stop with an error. If you get your constraints wrong, then you’re not going to get the results you want. Either you forget about a constraint that’s needed in which case you could compile C# code that fails at runtime or you add too many constraints that prevent you from compiling code that would have run just fine.
Because C++ looks at how you’re actually using the type being provided for T, it’s more accurate. You could have some class that’s never heard of the tradable class but just happens to have a method to get its original value. C++ would be happy to let you use this class because it meets the needs of the template code.
A lot of template code is used with algorithms. We’ll talk about algorithms in a future episode. For now know that many algorithms need to be able to compare instances to see if one instance should come before or after another. The algorithm might use the operator less than to make this comparison. In C++ you can make use of this template algorithm as long as your class has the required operator less than. You don’t need to create a special virtual abstract class or interface that declares operator less than is part of that interface. And you don’t need to try expressing a constraint that some comparable interface is required. Just make sure your class implements the methods that the template code will be using and the compiler will let you use it to replace type T.
There’s a completely different way to use templates not just with types but with values that I’ll explain right after this message from our sponsor. I’m still looking for one or more appropriate sponsors other than myself and have turned down companies wanting to sponsor this show because I felt that the ads would not directly benefit you. When I eventually find the right sponsor, it’ll be for a company or product that I feel will benefit you to know about.
( Message from Sponsor )
The C++ language had templates many years before C# was created and it wasn’t until C# version 2 that generics were introduced. When C# first introduced generics, I was surprised to find that the ability to specify values as template parameters was missing.
It’s not a very common practice even in C++ to use values as template parameters but when you learn what they can do for you, you may never look at C# generics quite the same way again.
Remember that one of your biggest goals when programming should be to use the compiler as much as possible to help you find bugs. The compiler is excellent at keeping types straight. If you have a method that records an adventurer’s position on a map, then that method might need a point class to express x and y coordinates. And you might also sometimes need an advanced version of your point class that can track three coordinates, x, y, and z.
You don’t want to get these point classes mixed up so a good way to do this is to create them as separate types. So you create 2DPoint and 3DPoint classes. Any method that needs a 2DPoint instance will declare in the method signature that one of its parameters is a 2DPoint type. And any method that works with three dimensions will declare one of its parameters to be of type 3DPoint.
So far there’s nothing special about all this except that when writing the 2DPoint and 3DPoint classes you have to decide if you’re going to use inheritance or not. After all, a 3DPoint class could be considered to be a 2DPoint class with just an extra coordinate. Normally, that would be a good approach. But if you use inheritance, then it means that 3DPoint instances can be passed to methods that expect 2DPoints because after all a 3DPoint is-a 2DPoint, right?
And what if you choose to not use inheritance? Then you’re going to have duplicate code in both classes. Now, maybe you can get around the duplicate code with either private inheritance or with containment but that leads to a more complicated design. Especially if you find out that you now need points with 4 or 5 dimensions. Check out episode #26 about when to use private inheritance if you want to brush up on this topic.
Another option is to write a single point class that can handle any number of points. this solves the code duplication and the complicated designs but then exposes you to the problem of some other code passing a point class with the wrong number of points to one of your methods. Since there’s just a single point class, the compiler can’t tell the difference between a point class with 2 coordinates vs. a point class with 3 coordinates because the number of coordinates is configured through data. And the compiler only looks at types.
What you need is some way to write a single point class that can work with any number of points but at the same time treat each of these as different types. This is where C++ helps by allowing you to create templates that are specialized not just by types but also by values. By creating a template that takes an integer value as one of its template parameters, you can write a single point class that’s typed specifically to the value of the integer.
With this, some code that wants to use your point class will put a number value as one of the template arguments so the code can declare that it wants a point of 2 variable and later maybe a point of 3. These are now distinct types and the compiler can help you make sure that they don’t get mixed up.