How do C# generics compare to C++ templates?

Q: How do C# generics compare to C++ templates?

A: This is really a fairly complex topic.

Anders has touched on it in an interview.

I should state at the outset that the goals of generics are not the same as the goals of templates. There are some things that templates do better than generics, and vice versa.

Model

C++ templates use a compile-time model. When a template is used in a C++ program, the effect is as if a sophisticated macro processor had been used.

C# generics are not just a feature of the compiler, but also a feature of the runtime. A generic type such as List<T> maintains its generic-ness (genericity) after it has been compiled. Or, to look at it another way, the substitution that the C++ compiler does at compile time is done at JIT time in the C# generic world.

Error Checking

Error is best illustrated through an example. Consider a template that has a method like this;

T Add(T t1, Tt2)
{
    return t1 + t2;
}

the C++ compiler will be able to parse this method successfully. When this template is actually used, the Ts will be replaced with an actual type. If a type such as int is used, the compiler will be able to create the Add method correctly, as it knows how to add two ints.

If a type such as Employee was used, the compiler would issue an error, as the compiler would know that there was no way to add two Employees.

The generics world is very different. Because the type that is used with the generic is not known at compile time, the compiler needs additional information about the type that will be used with a generic class.

This is done through constraints, which allow the author to constrain the types that can be used with a generic type.

For example:

Class List<T> where T:IComparable

means that whenever I use a T in my implementation, I can call the CompareTo() function on it.

Constraints could theoretically provide nearly the same level of flexibility that templates do, but that would require a very complex constraint syntax. For the Whidbey release, only certain operations can be specified through contraints, which limits the number of operations you can perform.

For example, there is no way to say that a generic type must have an add operator, so you can’t write “a + b“ in a generic class.

It is possible to work around this at runtime using reflection, but the implementation isn’t as clean and there may be a performance loss. We may address some of these issues in future releases

Run-time operations

Generics in C# have full run-time support. If you use reflection, you will find that you can reflect over generic types, and create them at runtime. There’s no real analog of this in the C++ world.

Space Use

The use of space is different between C++ and C#.  Because C++ templates are done at compile time, each use of a different type in a template results in a separate chunk of code being created by the compiler.

In the C# world, it’s somewhat different. The actual implementations using a specific type are created at runtime. When the runtime creates a type like List<int>, the JIT will see if that has already been created. If it has, it merely users that code. If not, it will take the IL that the compiler generated and do appropriate replacements with the actual type.

That’s not quite correct. There is a separate native code path for every value type, but since reference types are all reference-sized, they can share their implementation.

This means that the C# approach should have a smaller footprint on disk, and in memory, so that’s an advantage for generics over C++ templates.

In fact, the C++ linker implements a feature known as “template folding“, where the linker looks for native code sections that are identical, and if it finds them, folds them together. So it’s not a clear-cut as it would seem to be.

Template metaprogramming

C++ templates are sometimes used for a technique known as template metaprogramming. There is no way to do this in C#.

[Author: Eric Gunnerson]