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]
Comments
Anonymous
March 12, 2004
Why would you use generics if you limit your types to classes implementing a single interface? Why wouldn't you just use that interface directly? So instead of:
Class List<T> where T:IComparable
{
void Add(T Item) {...};
}
you'd use:
Class List
{
void Add(IComparable Item) {...};
}
The resulting effect is the same, it's faster, the code is easier to read and thanks to refactoring code it's not such a big deal to change your underlying interface.
The only reason to use generics is when valid types do not share a common base class (read other then Object).Anonymous
March 12, 2004
The comment has been removedAnonymous
March 12, 2004
The comment has been removedAnonymous
March 12, 2004
The comment has been removedAnonymous
March 13, 2004
Hmmmm.Anonymous
March 13, 2004
The comment has been removedAnonymous
March 13, 2004
Eric, I was talking about generics with type constraints. I understand what generics are and can do and I use them (quite a lot) in my C++ code but I don't understand why the concept of type constraints was introduced, if you don't want to use generics but rather work with a single type you can just use interfaces.Anonymous
March 14, 2004
Thoughts on genericsAnonymous
March 14, 2004
The comment has been removedAnonymous
March 15, 2004
I've just write a trick about operators and generics in my weblog. Maybe you want to read it and send your opinions.Anonymous
March 17, 2004
There are a number of other generic programming techniques that you can do with C++ templates (and generics in other languages like Haskell), that you can't do with .NET or Java generics. See this paper for details:
http://www.osl.iu.edu/publications/pubs/2003/comparing_generic_programming03.pdfAnonymous
March 17, 2004
The comment has been removedAnonymous
March 25, 2004
I think everyone here has made some good points about the “generics with constraints” versus “interfaces” debate and like most equivalent tools there’s a time to use one over the other.
It seems to me that you’d want to use “generics with constraints” when type safety is important (i.e. you want to make sure everything is the same type). However, sometimes this isn’t flexible enough (for example you want to be able to plug in different types at runtime) and that’s when you’d use interfaces.
Granted, I’m sure you could manipulate generics to do the latter, but I don’t clearly see how this could be done without some sort of reflection, and that would make the code confusing.Anonymous
April 01, 2004
one thing I didn't make clear in my previous comment is that constraints are useful to the implementation of things like generic sorted list or generic binary tree because it only makes sense to put something sortable into such data structures. and constraints (IComparable) are the way to guaranttee that you only get a strongly typed sorted list of a particular sortable (comparable) item. anything non-sortable (doesn't implement IComparable) cannot be used as the base type for a sorted list.Anonymous
April 27, 2004
The comment has been removedAnonymous
December 03, 2007
I came across an interesting quote from Tony Northrup regarding .net Generics: “I haven’t been able toAnonymous
December 03, 2007
I came across an interesting quote from Tony Northrup regarding .net Generics: “I haven’t been able toAnonymous
June 07, 2009
PingBack from http://greenteafatburner.info/story.php?id=2982Anonymous
June 08, 2009
PingBack from http://forums.ngemu.com/web-development-programming/123240-your-way-saving-changes-c-application.html#post1669911Anonymous
June 08, 2009
PingBack from http://quickdietsite.info/story.php?id=5467Anonymous
September 27, 2011
download-mobile-software.blogspot.comAnonymous
November 29, 2011
The difference can be demonstrated in the following C# codes: class B { public String G() { return "B.G()"; } } class D : B { public String G() { return "D.G()"; } } class TestCompile { private static String TestG<T>(T b) where T: B { return b.G(); } static void Main(string[] args) { TestG(new D()); } } The result is B.G(), whereas the result of similar C++ code would be D.G(). It seems that "where T: B" instantiates TestG<B> and applies it everywhere. If this is true, generic is quite less useful than template.